/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::str::FromStr; use std::sync::LazyLock; use std::time::Duration; use constellation_traits::NavigationHistoryBehavior; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; use js::rust::HandleObject; use regex::bytes::Regex; use servo_url::ServoUrl; use style::str::HTML_SPACE_CHARACTERS; use crate::dom::attr::Attr; use crate::dom::bindings::codegen::Bindings::HTMLMetaElementBinding::HTMLMetaElementMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::document::{DeclarativeRefresh, Document, determine_policy_for_token}; use crate::dom::element::{AttributeMutation, Element}; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlheadelement::HTMLHeadElement; use crate::dom::location::NavigationType; use crate::dom::node::{BindContext, Node, NodeTraits, UnbindContext}; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::window::Window; use crate::script_runtime::CanGc; use crate::timers::OneshotTimerCallback; #[dom_struct] pub(crate) struct HTMLMetaElement { htmlelement: HTMLElement, } #[derive(JSTraceable, MallocSizeOf)] pub(crate) struct RefreshRedirectDue { #[no_trace] pub(crate) url: ServoUrl, #[ignore_malloc_size_of = "non-owning"] pub(crate) window: DomRoot, } impl RefreshRedirectDue { pub(crate) fn invoke(self, can_gc: CanGc) { self.window.Location().navigate( self.url.clone(), NavigationHistoryBehavior::Replace, NavigationType::DeclarativeRefresh, can_gc, ); } } impl HTMLMetaElement { fn new_inherited( local_name: LocalName, prefix: Option, document: &Document, ) -> HTMLMetaElement { HTMLMetaElement { htmlelement: HTMLElement::new_inherited(local_name, prefix, document), } } #[cfg_attr(crown, allow(crown::unrooted_must_root))] pub(crate) fn new( local_name: LocalName, prefix: Option, document: &Document, proto: Option, can_gc: CanGc, ) -> DomRoot { Node::reflect_node_with_proto( Box::new(HTMLMetaElement::new_inherited(local_name, prefix, document)), document, proto, can_gc, ) } fn process_attributes(&self) { let element = self.upcast::(); if let Some(ref name) = element.get_name() { let name = name.to_ascii_lowercase(); let name = name.trim_matches(HTML_SPACE_CHARACTERS); if name == "referrer" { self.apply_referrer(); } // https://html.spec.whatwg.org/multipage/#attr-meta-http-equiv } else if !self.HttpEquiv().is_empty() { // TODO: Implement additional http-equiv candidates match self.HttpEquiv().to_ascii_lowercase().as_str() { "refresh" => self.declarative_refresh(), "content-security-policy" => self.apply_csp_list(), _ => {}, } } } fn process_referrer_attribute(&self) { let element = self.upcast::(); if let Some(ref name) = element.get_name() { let name = name.to_ascii_lowercase(); let name = name.trim_matches(HTML_SPACE_CHARACTERS); if name == "referrer" { self.apply_referrer(); } } } /// fn apply_referrer(&self) { let doc = self.owner_document(); // From spec: For historical reasons, unlike other standard metadata names, the processing model for referrer // is not responsive to element removals, and does not use tree order. Only the most-recently-inserted or // most-recently-modified meta element in this state has an effect. // 1. If element is not in a document tree, then return. let meta_node = self.upcast::(); if !meta_node.is_in_a_document_tree() { return; } // 2. If element does not have a name attribute whose value is an ASCII case-insensitive match for "referrer", // then return. if self.upcast::().get_name() != Some(atom!("referrer")) { return; } // 3. If element does not have a content attribute, or that attribute's value is the empty string, then return. let content = self .upcast::() .get_attribute(&ns!(), &local_name!("content")); if let Some(attr) = content { let attr = attr.value(); let attr_val = attr.trim(); if !attr_val.is_empty() { doc.set_referrer_policy(determine_policy_for_token(attr_val)); } } } /// fn apply_csp_list(&self) { if let Some(parent) = self.upcast::().GetParentElement() { if let Some(head) = parent.downcast::() { head.set_content_security_policy(); } } } /// fn declarative_refresh(&self) { // 2 let content = self.Content(); // 1 if !content.is_empty() { // 3 self.shared_declarative_refresh_steps(content); } } /// fn shared_declarative_refresh_steps(&self, content: DOMString) { // 1 let document = self.owner_document(); if document.will_declaratively_refresh() { return; } // 2-11 static REFRESH_REGEX: LazyLock = LazyLock::new(|| { Regex::new( r#"(?x) ^ \s* # 3 ((?