Repository: servo/servo
File: components/script/links.rs (Rust)

Code before:
pub(crate) fn get_element_target(
    subject: &Element,
    target: Option<DOMString>,
) -> Option<DOMString> {
    assert!(
        subject.is::<HTMLAreaElement>() ||
            subject.is::<HTMLAnchorElement>() ||
            subject.is::<HTMLFormElement>()
    );

    // Step 1. If target is null, then:
    let target = target.or_else(|| {
        // Step 1.1. If element has a target attribute, then set target to that attribute's value.
        //
        // Note that for a target attribute to be valid, it must be a valid navigable target name
        // or keyword
        let element_target = subject.get_string_attribute(&local_name!("target"));
        if valid_navigable_target_name_or_keyword(&element_target) {
            Some(element_target)
        } else {
            // Step 1.2. Otherwise, if element's node document contains a base element with a target attribute,
            // set target to the value of the target attribute of the first such base element.
            subject
                .owner_document()
                .upcast::<Node>()
                .traverse_preorder(ShadowIncluding::No)
                .filter_map(DomRoot::downcast::<HTMLBaseElement>)
                .next()
                .and_then(|base_element| {
                    let element = base_element.upcast::<Element>();
                    if element.has_attribute(&local_name!("target")) {
                        Some(element.get_string_attribute(&local_name!("target")))
                    } else {
                        None
                    }
                })
        }
    });
    // Step 2. If target is not null, and contains an ASCII tab or newline and a U+003C (<), then set target to "_blank".
    if let Some(ref target) = target {
        if target.contains_tab_or_newline() && target.contains("\u{003C}") {
            return Some("_blank".into());
        }
    }
    // Step 3. Return target.
    target
}

/// <https://html.spec.whatwg.org/multipage/#following-hyperlinks-2>
pub(crate) fn follow_hyperlink(
    subject: &Element,

Review comment (performance):
I'm nervous about adding a full tree traversal in the common case for getting an element's target. Base elements are fairly uncommon, I think, so this could be a big performance hit for large pages.

Code after:
    target: Option<DOMString>,
) -> Option<DOMString> {
    assert!(
        subject.is::<HTMLAreaElement>() ||
            subject.is::<HTMLAnchorElement>() ||
            subject.is::<HTMLFormElement>()
    );

    // Step 1. If target is null, then:
    let target = target.or_else(|| {
        // Step 1.1. If element has a target attribute, then set target to that attribute's value.
        //
        // Note that for a target attribute to be valid, it must be a valid navigable target name
        // or keyword
        let element_target = subject.get_string_attribute(&local_name!("target"));
        if valid_navigable_target_name_or_keyword(&element_target) {
            Some(element_target)
        } else {
            // Step 1.2. Otherwise, if element's node document contains a base element with a target attribute,
            // set target to the value of the target attribute of the first such base element.
            subject
                .owner_document()
                .target_base_element()
                .and_then(|base_element| {
                    let element = base_element.upcast::<Element>();
                    if element.has_attribute(&local_name!("target")) {
                        Some(element.get_string_attribute(&local_name!("target")))
                    } else {
                        None
                    }
                })
        }
    });
    // Step 2. If target is not null, and contains an ASCII tab or newline and a U+003C (<), then set target to "_blank".
    if let Some(ref target) = target {
        if target.contains_tab_or_newline() && target.contains("\u{003C}") {
            return Some("_blank".into());
        }
    }
    // Step 3. Return target.
    target
}

/// <https://html.spec.whatwg.org/multipage/#following-hyperlinks-2>
pub(crate) fn follow_hyperlink(
    subject: &Element,
    relations: LinkRelations,
    hyperlink_suffix: Option<String>,
) {
    // Step 1: If subject cannot navigate, then return.
    if subject.cannot_navigate() {