Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upImplemented get element target algorithm #27299
Conversation
highfive
commented
Jul 16, 2020
|
Thanks for the pull request, and welcome! The Servo team is excited to review your changes, and you should hear from @SimonSapin (or someone else) soon. |
highfive
commented
Jul 16, 2020
|
Heads up! This PR modifies the following files:
|
highfive
commented
Jul 16, 2020
|
I think the error says Incorrect License for a source file. Any idea what that means? |
b262242
to
eac6011
|
@jdm I think this looks good. Is there something I'm missing in the code? |
|
Sorry, I got distracted from this. I'll review it tomorrow! |
|
Good start! |
| @@ -581,21 +582,39 @@ pub fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>) { | |||
| let source = document.browsing_context().unwrap(); | |||
|
|
|||
| // Step 4-5: target attribute. | |||
| let target_attribute_value = subject.get_attribute(&ns!(), &local_name!("target")); | |||
|
|
|||
| let subject_type = subject.get_attribute(&ns!(), &local_name!("target")); | |||
This comment has been minimized.
This comment has been minimized.
jdm
Jul 21, 2020
Member
My preference would be to extract this into a separate function that has a comment linking to https://html.spec.whatwg.org/multipage/semantics.html#get-an-element's-target and invoke that, so that this code more clearly follows the specification.
| if !subject.has_attribute(&local_name!("target")) { | ||
| let base = (*document).base_element(); | ||
| match base { | ||
| Some(base) => { | ||
| let base_html_unwrap = (*base).upcast::<HTMLElement>().upcast::<Element>(); | ||
| if base_html_unwrap.has_attribute(&local_name!("target")) { | ||
| target_attribute_value = | ||
| base_html_unwrap.get_attribute(&ns!(), &local_name!("target")); | ||
| } else { | ||
| target_attribute_value = None; | ||
| } | ||
| }, | ||
| None => target_attribute_value = None, | ||
| }; | ||
| } | ||
| } |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 21, 2020
Member
With a get_element_target function that returns Option<DOMString>, we could write this as:
if !(subject.is::<HTMLAreaElement>() || subject.is::<HTMLAnchorElement>() || subject.is::<HTMLFormElement>()) {
return None;
}
if subject.has_attribute(&local_name!("target")) {
return subject.get_attribute(&ns!(), &local_name!("target"));
}
document
.base_element()
.map(|base| base.upcast::<Element>())
.and_then(|element| {
if element.has_attribute(&local_name!("target")) {
Some(element.get_attribute(&local_name!("target")))
} else {
None
}
})| let noopener = if let Some(link_types) = subject.get_attribute(&ns!(), &local_name!("rel")) { | ||
| let values = link_types.Value(); | ||
| let contains_noopener = values.contains("noopener"); | ||
| let contains_noreferrer = values.contains("noreferrer"); | ||
| let contains_opener = values.contains("opener"); | ||
| let target_is_blank = if let Some(name) = target_attribute_value.as_ref() { | ||
| name.Value().to_lowercase() == "_blank" | ||
| } else { | ||
| false | ||
| }; | ||
|
|
||
| contains_noreferrer || contains_noopener || (!contains_opener && target_is_blank) | ||
| values.contains("noreferrer") || | ||
| values.contains("noopener") || | ||
| (!values.contains("opener") && target_is_blank) | ||
| } else { | ||
| false | ||
| }; |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 21, 2020
Member
This would also be good to extract into a function that links to https://html.spec.whatwg.org/multipage/links.html#get-an-element's-noopener and splits the string value appropriately (https://html.spec.whatwg.org/multipage/links.html#linkTypes)
| let target_attribute_value; | ||
| let subject = submitter.form_owner().unwrap(); | ||
| if submitter.is_submit_button() && submitter.target() != DOMString::new() { | ||
| target_attribute_value = action; | ||
| } else { | ||
| // the result of getting an element's target given submitter's form owner. | ||
| if subject | ||
| .upcast::<HTMLElement>() | ||
| .upcast::<Element>() | ||
| .has_attribute(&local_name!("target")) | ||
| { | ||
| target_attribute_value = subject | ||
| .upcast::<HTMLElement>() | ||
| .upcast::<Element>() | ||
| .get_attribute(&ns!(), &local_name!("target")) | ||
| .unwrap() | ||
| .Value(); | ||
| } else { | ||
| let base = | ||
| (*document_from_node(subject.upcast::<HTMLElement>().upcast::<Element>())) | ||
| .base_element(); | ||
| match base { | ||
| Some(base) => { | ||
| let base_html_unwrap = (*base).upcast::<HTMLElement>().upcast::<Element>(); | ||
| if base_html_unwrap.has_attribute(&local_name!("target")) { | ||
| target_attribute_value = (*base_html_unwrap | ||
| .get_attribute(&ns!(), &local_name!("target")) | ||
| .unwrap()) | ||
| .Value(); | ||
| } else { | ||
| target_attribute_value = DOMString::new(); | ||
| } | ||
| }, | ||
| None => target_attribute_value = DOMString::new(), | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| // Step 18 | ||
| let noopener = if let Some(form_data) = subject | ||
| .upcast::<HTMLElement>() | ||
| .upcast::<Element>() | ||
| .get_attribute(&ns!(), &local_name!("rel")) | ||
| { | ||
| let values = form_data.Value(); | ||
| let target_is_blank: bool = | ||
| target_attribute_value.to_string().to_lowercase() == "_blank".to_string(); | ||
| values.contains("noreferrer") || | ||
| values.contains("noopener") || | ||
| (!values.contains("opener") && target_is_blank) | ||
| } else { | ||
| false | ||
| }; |
This comment has been minimized.
This comment has been minimized.
|
Getting close! |
| if (document_from_node(subject).base_element()) | ||
| .unwrap() |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 22, 2020
Member
We don't want to panic if there is no <base>, so let's do this:
document_from_node(subject)
.base_element()
.map(|base| base.upcast::<Element>())
.and_then(|element| {
if element.has_attribute(&local_name!("target")) {
Some(element.get_string_attribute(&local_name!("target")))
} else {
None
}
})
This comment has been minimized.
This comment has been minimized.
avr1254
Jul 22, 2020
Author
Contributor
I get an error code E0515 about not being able to return a value referencing function parameter 'base'
This comment has been minimized.
This comment has been minimized.
avr1254
Jul 22, 2020
Author
Contributor
@jdm Would using a match statement instead of unwrapping the base_element() value eliminate the chance for a panic?
This comment has been minimized.
This comment has been minimized.
jdm
Jul 22, 2020
Member
It would. It may also be possible to do .base_element().as_ref(), or remove the map and do let element = element.upcast::<Element>(); inside of the and_then.
| subject | ||
| .get_attribute(&ns!(), &local_name!("target")) | ||
| .unwrap() | ||
| .Value(), |
This comment has been minimized.
This comment has been minimized.
| { | ||
| return false; | ||
| } | ||
| let link_types = subject.get_attribute(&ns!(), &local_name!("rel")).unwrap(); |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 22, 2020
Member
To avoid panicking, let's do:
let link_types = match subject.get_attribute(&ns!(), &local_name!("rel")) {
Some(rel) => rel.Value(),
None => return false,
};| let target_is_blank = if let Some(name) = target_attribute_value.as_ref() { | ||
| name.to_lowercase() == "_blank" | ||
| } else { | ||
| false | ||
| }; |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 22, 2020
Member
let target_is_blank = target_attribute_value.as_ref().map_or(false, |target| target.to_lowercase() == "_blank");
| if values.contains("noreferrer") || | ||
| values.contains("noopener") || | ||
| (!values.contains("opener") && target_is_blank) | ||
| { | ||
| return true; | ||
| } else { | ||
| return false; | ||
| } |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 22, 2020
Member
We can return the condition directly; there's no need for the explicit return true or return false here.
| let mut target_attribute_value = None; | ||
| if subject.is::<HTMLAreaElement>() || subject.is::<HTMLAnchorElement>() { | ||
| target_attribute_value = get_element_target(subject); | ||
| } |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 22, 2020
Member
Let's do:
let target_attribute_value = if subject.is::<...> ... {
get_element_target(subject)
} else {
None
};| target_attribute_value = Some(action); | ||
| } else { | ||
| target_attribute_value = | ||
| get_element_target(&subject.upcast::<HTMLElement>().upcast::<Element>()); |
This comment has been minimized.
This comment has been minimized.
| let target_attribute_value: Option<DOMString>; | ||
| let subject = submitter.form_owner().unwrap(); | ||
| if submitter.is_submit_button() && submitter.target() != DOMString::new() { | ||
| target_attribute_value = Some(action); |
This comment has been minimized.
This comment has been minimized.
|
|
||
| // Step 18 | ||
| let noopener = get_element_noopener( | ||
| &subject.upcast::<HTMLElement>().upcast::<Element>(), |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
|
| @@ -0,0 +1,3 @@ | |||
| [opener-closed.html] | |||
This comment has been minimized.
This comment has been minimized.
jdm
Jul 24, 2020
Member
Aha - this file should be in the auxiliary-browsing-contexts subdirectory of browsers/windows/. That's why it's not being picked up correctly.
This comment has been minimized.
This comment has been minimized.
|
@bors-servo r+ |
|
|
|
@jdm Thanks for all the help and mentoring on this PR! |
|
Thanks for sticking with it! |
Implemented get element target algorithm Added check for area and anchor element Finished issue: Implemented get target and no opener algorithm Implemented get element target and get element noopener algorithms. <!-- Please describe your changes on the following line: --> Used the algorithms in html spec to implement target and no opener algorithms. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #27253 (GitHub issue number if applicable) <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
|
|
| [opener-closed.html] | ||
| [An auxiliary browsing context should report `null` for `window.opener` when that browsing context is discarded] | ||
| expected: TIMEOUT |
This comment has been minimized.
This comment has been minimized.
jdm
Jul 24, 2020
Member
We're missing a TIMEOUT for the top-level test file, too:
[opener-closed.html]
expected: TIMEOUT
[An auxiliary browsing context should report `null` for `window.opener` when that browsing context is discarded]
expected: TIMEOUT
This comment has been minimized.
This comment has been minimized.
and refactored into functions.
|
@bors-servo r+ |
|
|
|
|
avr1254 commentedJul 16, 2020
•
edited by atouchet
Added check for area and anchor element
Finished issue: Implemented get target and no opener algorithm
Implemented get element target and get element noopener algorithms.
Used the algorithms in html spec to implement target and no opener algorithms.
./mach build -ddoes not report any errors./mach test-tidydoes not report any errors