script: Implement <meta http-equiv="refresh"> (#31468)

* script: Implement <meta http-equiv="refresh">

* Address review comments
This commit is contained in:
Smitty 2024-03-01 02:42:18 -05:00 committed by GitHub
parent ee122acdf4
commit 0beec63c86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 227 additions and 396 deletions

View file

@ -2,29 +2,57 @@
* 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 dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use js::rust::HandleObject;
use regex::bytes::Regex;
use script_traits::{HistoryEntryReplacement, MsDuration};
use servo_url::ServoUrl;
use style::str::HTML_SPACE_CHARACTERS;
use crate::dom::attr::Attr;
use crate::dom::bindings::cell::DomRefCell;
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::Document;
use crate::dom::document::{DeclarativeRefresh, Document};
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlheadelement::HTMLHeadElement;
use crate::dom::node::{BindContext, Node, UnbindContext};
use crate::dom::location::NavigationType;
use crate::dom::node::{document_from_node, window_from_node, BindContext, Node, UnbindContext};
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window;
use crate::timers::OneshotTimerCallback;
#[dom_struct]
pub struct HTMLMetaElement {
htmlelement: HTMLElement,
}
#[derive(JSTraceable, MallocSizeOf)]
pub struct RefreshRedirectDue {
#[no_trace]
pub url: ServoUrl,
#[ignore_malloc_size_of = "non-owning"]
pub window: DomRoot<Window>,
}
impl RefreshRedirectDue {
pub fn invoke(self) {
self.window.Location().navigate(
self.url.clone(),
HistoryEntryReplacement::Enabled,
NavigationType::DeclarativeRefresh,
);
}
}
impl HTMLMetaElement {
fn new_inherited(
local_name: LocalName,
@ -58,6 +86,8 @@ impl HTMLMetaElement {
if name == "referrer" {
self.apply_referrer();
}
} else if &*self.HttpEquiv() != "" {
self.declarative_refresh();
}
}
@ -81,6 +111,96 @@ impl HTMLMetaElement {
}
}
}
/// <https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps>
fn declarative_refresh(&self) {
// 2
let content = self.Content();
// 1
if !content.is_empty() {
// 3
self.shared_declarative_refresh_steps(content);
}
}
/// <https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps>
fn shared_declarative_refresh_steps(&self, content: DOMString) {
// 1
let document = document_from_node(self);
if document.will_declaratively_refresh() {
return;
}
// 2-11
lazy_static::lazy_static! {
static ref REFRESH_REGEX: Regex = Regex::new(
r#"(?x)
^
\s* # 3
((?<time>\d+)\.?|\.) # 5-6
[0-9.]* # 8
(
(;|,| ) # 10.1
\s* # 10.2
(;|,)? # 10.3
\s* # 10.4
(
(U|u)(R|r)(L|l) # 11.2-11.4
\s*=\s* # 11.5-11.7
('(?<url1>.*?)'?|"(?<url2>.*?)"?|(?<url3>[^'"].*)) # 11.8 - 11.10
|
(?<url4>.*)
)?
)?
$
"#,
)
.unwrap();
}
let mut url_record = document.url();
let captures = if let Some(captures) = REFRESH_REGEX.captures(content.as_bytes()) {
captures
} else {
return;
};
let time = if let Some(time_string) = captures.name("time") {
u64::from_str(&String::from_utf8_lossy(time_string.as_bytes())).unwrap_or(0)
} else {
0
};
let captured_url = captures.name("url1").or(captures
.name("url2")
.or(captures.name("url3").or(captures.name("url4"))));
if let Some(url_match) = captured_url {
url_record = if let Ok(url) = ServoUrl::parse_with_base(
Some(&url_record),
&String::from_utf8_lossy(url_match.as_bytes()),
) {
url
} else {
return;
}
}
// 12-13
if document.completely_loaded() {
// TODO: handle active sandboxing flag
let window = window_from_node(self);
window.upcast::<GlobalScope>().schedule_callback(
OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue {
window: window.clone(),
url: url_record,
}),
MsDuration::new(time.saturating_mul(1000)),
);
document.set_declarative_refresh(DeclarativeRefresh::CreatedAfterLoad);
} else {
document.set_declarative_refresh(DeclarativeRefresh::PendingLoad {
url: url_record,
time,
});
}
}
}
impl HTMLMetaElementMethods for HTMLMetaElement {
@ -95,6 +215,11 @@ impl HTMLMetaElementMethods for HTMLMetaElement {
// https://html.spec.whatwg.org/multipage/#dom-meta-content
make_setter!(SetContent, "content");
// https://html.spec.whatwg.org/multipage/#dom-meta-httpequiv
make_getter!(HttpEquiv, "http-equiv");
// https://html.spec.whatwg.org/multipage/#dom-meta-httpequiv
make_atomic_setter!(SetHttpEquiv, "http-equiv");
}
impl VirtualMethods for HTMLMetaElement {