mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Auto merge of #14971 - asajeffrey:script-track-active-documents, r=cbrewster
Constellation informs script about documents becoming inactive, active or fully active. <!-- Please describe your changes on the following line: --> This PR replaces the current freeze/thaw mechanism by messages from the constellation to script informing it about when documents become inactive, active or fully active. This means we can now implement |Document::is_active()| which is used in |document.write|. This PR also changes how timers work: previously they were initialized running, and were then frozen/thawed. This means there was a transitory period when timers were running even though the document was not fully active. With this PR, timers are initially suspended, and are only resumed when the document is made fully active. --- <!-- 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 #14876 - [X] These changes do not require tests because it's an interal refactoring. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14971) <!-- Reviewable:end -->
This commit is contained in:
commit
b5c94bad37
18 changed files with 210 additions and 100 deletions
|
@ -74,7 +74,7 @@ use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
|||
use script_layout_interface::OpaqueStyleAndLayoutData;
|
||||
use script_layout_interface::reporter::CSSErrorReporter;
|
||||
use script_layout_interface::rpc::LayoutRPC;
|
||||
use script_traits::{TimerEventId, TimerSource, TouchpadPressurePhase};
|
||||
use script_traits::{DocumentActivity, TimerEventId, TimerSource, TouchpadPressurePhase};
|
||||
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use servo_atoms::Atom;
|
||||
|
@ -327,7 +327,7 @@ unsafe_no_jsmanaged_fields!(TrustedPromise);
|
|||
unsafe_no_jsmanaged_fields!(PropertyDeclarationBlock);
|
||||
// These three are interdependent, if you plan to put jsmanaged data
|
||||
// in one of these make sure it is propagated properly to containing structs
|
||||
unsafe_no_jsmanaged_fields!(FrameId, FrameType, WindowSizeData, WindowSizeType, PipelineId);
|
||||
unsafe_no_jsmanaged_fields!(DocumentActivity, FrameId, FrameType, WindowSizeData, WindowSizeType, PipelineId);
|
||||
unsafe_no_jsmanaged_fields!(TimerEventId, TimerSource);
|
||||
unsafe_no_jsmanaged_fields!(TimelineMarkerType);
|
||||
unsafe_no_jsmanaged_fields!(WorkerId);
|
||||
|
|
|
@ -108,7 +108,8 @@ use origin::Origin;
|
|||
use script_layout_interface::message::{Msg, ReflowQueryType};
|
||||
use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
|
||||
use script_thread::{MainThreadScriptMsg, Runnable};
|
||||
use script_traits::{AnimationState, CompositorEvent, MouseButton, MouseEventType, MozBrowserEvent};
|
||||
use script_traits::{AnimationState, CompositorEvent, DocumentActivity};
|
||||
use script_traits::{MouseButton, MouseEventType, MozBrowserEvent};
|
||||
use script_traits::{ScriptMsg as ConstellationMsg, TouchpadPressurePhase};
|
||||
use script_traits::{TouchEventType, TouchId};
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
|
@ -191,7 +192,7 @@ pub struct Document {
|
|||
last_modified: Option<String>,
|
||||
encoding: Cell<EncodingRef>,
|
||||
is_html_document: bool,
|
||||
is_fully_active: Cell<bool>,
|
||||
activity: Cell<DocumentActivity>,
|
||||
url: DOMRefCell<ServoUrl>,
|
||||
quirks_mode: Cell<QuirksMode>,
|
||||
/// Caches for the getElement methods
|
||||
|
@ -387,17 +388,33 @@ impl Document {
|
|||
self.trigger_mozbrowser_event(MozBrowserEvent::SecurityChange(https_state));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#fully-active
|
||||
pub fn is_fully_active(&self) -> bool {
|
||||
self.is_fully_active.get()
|
||||
self.activity.get() == DocumentActivity::FullyActive
|
||||
}
|
||||
|
||||
pub fn fully_activate(&self) {
|
||||
self.is_fully_active.set(true)
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.activity.get() != DocumentActivity::Inactive
|
||||
}
|
||||
|
||||
pub fn fully_deactivate(&self) {
|
||||
self.is_fully_active.set(false)
|
||||
pub fn set_activity(&self, activity: DocumentActivity) {
|
||||
// This function should only be called on documents with a browsing context
|
||||
assert!(self.browsing_context.is_some());
|
||||
// Set the document's activity level, reflow if necessary, and suspend or resume timers.
|
||||
if activity != self.activity.get() {
|
||||
self.activity.set(activity);
|
||||
if activity == DocumentActivity::FullyActive {
|
||||
self.title_changed();
|
||||
self.dirty_all_nodes();
|
||||
self.window().reflow(
|
||||
ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::CachedPageNeededReflow
|
||||
);
|
||||
self.window().resume();
|
||||
} else {
|
||||
self.window().suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> &Origin {
|
||||
|
@ -1892,6 +1909,7 @@ impl Document {
|
|||
is_html_document: IsHTMLDocument,
|
||||
content_type: Option<DOMString>,
|
||||
last_modified: Option<String>,
|
||||
activity: DocumentActivity,
|
||||
source: DocumentSource,
|
||||
doc_loader: DocumentLoader,
|
||||
referrer: Option<String>,
|
||||
|
@ -1927,7 +1945,7 @@ impl Document {
|
|||
// https://dom.spec.whatwg.org/#concept-document-encoding
|
||||
encoding: Cell::new(UTF_8),
|
||||
is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
|
||||
is_fully_active: Cell::new(false),
|
||||
activity: Cell::new(activity),
|
||||
id_map: DOMRefCell::new(HashMap::new()),
|
||||
tag_map: DOMRefCell::new(HashMap::new()),
|
||||
tagns_map: DOMRefCell::new(HashMap::new()),
|
||||
|
@ -1995,6 +2013,7 @@ impl Document {
|
|||
IsHTMLDocument::NonHTMLDocument,
|
||||
None,
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::NotFromParser,
|
||||
docloader,
|
||||
None,
|
||||
|
@ -2008,6 +2027,7 @@ impl Document {
|
|||
doctype: IsHTMLDocument,
|
||||
content_type: Option<DOMString>,
|
||||
last_modified: Option<String>,
|
||||
activity: DocumentActivity,
|
||||
source: DocumentSource,
|
||||
doc_loader: DocumentLoader,
|
||||
referrer: Option<String>,
|
||||
|
@ -2020,6 +2040,7 @@ impl Document {
|
|||
doctype,
|
||||
content_type,
|
||||
last_modified,
|
||||
activity,
|
||||
source,
|
||||
doc_loader,
|
||||
referrer,
|
||||
|
@ -2093,6 +2114,7 @@ impl Document {
|
|||
doctype,
|
||||
None,
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::NotFromParser,
|
||||
DocumentLoader::new(&self.loader()),
|
||||
None,
|
||||
|
@ -3249,8 +3271,7 @@ impl DocumentMethods for Document {
|
|||
|
||||
// Step 2.
|
||||
// TODO: handle throw-on-dynamic-markup-insertion counter.
|
||||
// FIXME: this should check for being active rather than fully active
|
||||
if !self.is_fully_active() {
|
||||
if !self.is_active() {
|
||||
// Step 3.
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ use dom::htmltitleelement::HTMLTitleElement;
|
|||
use dom::node::Node;
|
||||
use dom::text::Text;
|
||||
use dom::xmldocument::XMLDocument;
|
||||
use script_traits::DocumentActivity;
|
||||
|
||||
// https://dom.spec.whatwg.org/#domimplementation
|
||||
#[dom_struct]
|
||||
|
@ -83,6 +84,7 @@ impl DOMImplementationMethods for DOMImplementation {
|
|||
IsHTMLDocument::NonHTMLDocument,
|
||||
Some(DOMString::from(content_type)),
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::NotFromParser,
|
||||
loader);
|
||||
// Step 2-3.
|
||||
|
@ -129,6 +131,7 @@ impl DOMImplementationMethods for DOMImplementation {
|
|||
IsHTMLDocument::HTMLDocument,
|
||||
None,
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::NotFromParser,
|
||||
loader,
|
||||
None,
|
||||
|
|
|
@ -19,6 +19,7 @@ use dom::document::{Document, IsHTMLDocument};
|
|||
use dom::document::DocumentSource;
|
||||
use dom::servoparser::ServoParser;
|
||||
use dom::window::Window;
|
||||
use script_traits::DocumentActivity;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct DOMParser {
|
||||
|
@ -65,6 +66,7 @@ impl DOMParserMethods for DOMParser {
|
|||
IsHTMLDocument::HTMLDocument,
|
||||
Some(content_type),
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::FromParser,
|
||||
loader,
|
||||
None,
|
||||
|
@ -82,6 +84,7 @@ impl DOMParserMethods for DOMParser {
|
|||
IsHTMLDocument::NonHTMLDocument,
|
||||
Some(content_type),
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::NotFromParser,
|
||||
loader,
|
||||
None,
|
||||
|
|
|
@ -65,6 +65,7 @@ use ref_slice::ref_slice;
|
|||
use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData};
|
||||
use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress};
|
||||
use script_layout_interface::message::Msg;
|
||||
use script_traits::DocumentActivity;
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use selectors::matching::{MatchingReason, matches};
|
||||
use selectors::parser::SelectorList;
|
||||
|
@ -1730,7 +1731,8 @@ impl Node {
|
|||
// https://github.com/whatwg/dom/issues/378
|
||||
document.origin().alias(),
|
||||
is_html_doc, None,
|
||||
None, DocumentSource::NotFromParser, loader,
|
||||
None, DocumentActivity::Inactive,
|
||||
DocumentSource::NotFromParser, loader,
|
||||
None, None);
|
||||
Root::upcast::<Node>(document)
|
||||
},
|
||||
|
|
|
@ -35,6 +35,7 @@ use network_listener::PreInvoke;
|
|||
use profile_traits::time::{TimerMetadata, TimerMetadataFrameType};
|
||||
use profile_traits::time::{TimerMetadataReflowType, ProfilerCategory, profile};
|
||||
use script_thread::ScriptThread;
|
||||
use script_traits::DocumentActivity;
|
||||
use servo_config::resource_files::read_resource_file;
|
||||
use servo_url::ServoUrl;
|
||||
use std::cell::Cell;
|
||||
|
@ -107,6 +108,7 @@ impl ServoParser {
|
|||
IsHTMLDocument::HTMLDocument,
|
||||
None,
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::FromParser,
|
||||
loader,
|
||||
None,
|
||||
|
|
|
@ -1488,21 +1488,17 @@ impl Window {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn freeze(&self) {
|
||||
pub fn suspend(&self) {
|
||||
self.upcast::<GlobalScope>().suspend();
|
||||
// A hint to the JS runtime that now would be a good time to
|
||||
// GC any unreachable objects generated by user script,
|
||||
// or unattached DOM nodes. Attached DOM nodes can't be GCd yet,
|
||||
// as the document might be thawed later.
|
||||
// as the document might be reactivated later.
|
||||
self.Gc();
|
||||
}
|
||||
|
||||
pub fn thaw(&self) {
|
||||
pub fn resume(&self) {
|
||||
self.upcast::<GlobalScope>().resume();
|
||||
|
||||
// Push the document title to the compositor since we are
|
||||
// activating this document due to a navigation.
|
||||
self.Document().title_changed();
|
||||
}
|
||||
|
||||
pub fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
|
||||
|
|
|
@ -17,6 +17,7 @@ use dom::node::Node;
|
|||
use dom::window::Window;
|
||||
use js::jsapi::{JSContext, JSObject};
|
||||
use origin::Origin;
|
||||
use script_traits::DocumentActivity;
|
||||
use servo_url::ServoUrl;
|
||||
|
||||
// https://dom.spec.whatwg.org/#xmldocument
|
||||
|
@ -33,6 +34,7 @@ impl XMLDocument {
|
|||
is_html_document: IsHTMLDocument,
|
||||
content_type: Option<DOMString>,
|
||||
last_modified: Option<String>,
|
||||
activity: DocumentActivity,
|
||||
source: DocumentSource,
|
||||
doc_loader: DocumentLoader) -> XMLDocument {
|
||||
XMLDocument {
|
||||
|
@ -43,6 +45,7 @@ impl XMLDocument {
|
|||
is_html_document,
|
||||
content_type,
|
||||
last_modified,
|
||||
activity,
|
||||
source,
|
||||
doc_loader,
|
||||
None,
|
||||
|
@ -57,6 +60,7 @@ impl XMLDocument {
|
|||
doctype: IsHTMLDocument,
|
||||
content_type: Option<DOMString>,
|
||||
last_modified: Option<String>,
|
||||
activity: DocumentActivity,
|
||||
source: DocumentSource,
|
||||
doc_loader: DocumentLoader)
|
||||
-> Root<XMLDocument> {
|
||||
|
@ -68,6 +72,7 @@ impl XMLDocument {
|
|||
doctype,
|
||||
content_type,
|
||||
last_modified,
|
||||
activity,
|
||||
source,
|
||||
doc_loader),
|
||||
window,
|
||||
|
|
|
@ -58,6 +58,7 @@ use net_traits::CoreResourceMsg::Fetch;
|
|||
use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode};
|
||||
use net_traits::trim_http_whitespace;
|
||||
use network_listener::{NetworkListener, PreInvoke};
|
||||
use script_traits::DocumentActivity;
|
||||
use servo_atoms::Atom;
|
||||
use servo_config::prefs::PREFS;
|
||||
use servo_url::ServoUrl;
|
||||
|
@ -1228,6 +1229,7 @@ impl XMLHttpRequest {
|
|||
is_html_document,
|
||||
content_type,
|
||||
None,
|
||||
DocumentActivity::Inactive,
|
||||
DocumentSource::FromParser,
|
||||
docloader,
|
||||
None,
|
||||
|
|
|
@ -83,7 +83,8 @@ use profile_traits::time::{self, ProfilerCategory, profile};
|
|||
use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType};
|
||||
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory, EnqueuedPromiseCallback};
|
||||
use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx, PromiseJobQueue};
|
||||
use script_traits::{CompositorEvent, ConstellationControlMsg, DiscardBrowsingContext, EventResult};
|
||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||
use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
|
||||
use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent};
|
||||
use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg};
|
||||
use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource};
|
||||
|
@ -148,8 +149,8 @@ struct InProgressLoad {
|
|||
window_size: Option<WindowSizeData>,
|
||||
/// Channel to the layout thread associated with this pipeline.
|
||||
layout_chan: Sender<message::Msg>,
|
||||
/// Window is frozen (navigated away while loading for example).
|
||||
is_frozen: bool,
|
||||
/// The activity level of the document (inactive, active or fully active).
|
||||
activity: DocumentActivity,
|
||||
/// Window is visible.
|
||||
is_visible: bool,
|
||||
/// The requested URL of the load.
|
||||
|
@ -172,7 +173,7 @@ impl InProgressLoad {
|
|||
parent_info: parent_info,
|
||||
layout_chan: layout_chan,
|
||||
window_size: window_size,
|
||||
is_frozen: false,
|
||||
activity: DocumentActivity::FullyActive,
|
||||
is_visible: true,
|
||||
url: url,
|
||||
origin: origin,
|
||||
|
@ -963,10 +964,8 @@ impl ScriptThread {
|
|||
self.handle_resize_inactive_msg(id, new_size),
|
||||
ConstellationControlMsg::GetTitle(pipeline_id) =>
|
||||
self.handle_get_title_msg(pipeline_id),
|
||||
ConstellationControlMsg::Freeze(pipeline_id) =>
|
||||
self.handle_freeze_msg(pipeline_id),
|
||||
ConstellationControlMsg::Thaw(pipeline_id) =>
|
||||
self.handle_thaw_msg(pipeline_id),
|
||||
ConstellationControlMsg::SetDocumentActivity(pipeline_id, activity) =>
|
||||
self.handle_set_document_activity_msg(pipeline_id, activity),
|
||||
ConstellationControlMsg::ChangeFrameVisibilityStatus(pipeline_id, visible) =>
|
||||
self.handle_visibility_change_msg(pipeline_id, visible),
|
||||
ConstellationControlMsg::NotifyVisibilityChange(parent_pipeline_id, frame_id, visible) =>
|
||||
|
@ -1303,37 +1302,20 @@ impl ScriptThread {
|
|||
warn!("change visibility message sent to nonexistent pipeline");
|
||||
}
|
||||
|
||||
/// Handles freeze message
|
||||
fn handle_freeze_msg(&self, id: PipelineId) {
|
||||
/// Handles activity change message
|
||||
fn handle_set_document_activity_msg(&self, id: PipelineId, activity: DocumentActivity) {
|
||||
debug!("Setting activity of {} to be {:?}.", id, activity);
|
||||
let document = self.documents.borrow().find_document(id);
|
||||
if let Some(document) = document {
|
||||
document.window().freeze();
|
||||
document.fully_deactivate();
|
||||
document.set_activity(activity);
|
||||
return;
|
||||
}
|
||||
let mut loads = self.incomplete_loads.borrow_mut();
|
||||
if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
|
||||
load.is_frozen = true;
|
||||
load.activity = activity;
|
||||
return;
|
||||
}
|
||||
warn!("freeze sent to nonexistent pipeline");
|
||||
}
|
||||
|
||||
/// Handles thaw message
|
||||
fn handle_thaw_msg(&self, id: PipelineId) {
|
||||
let document = self.documents.borrow().find_document(id);
|
||||
if let Some(document) = document {
|
||||
self.rebuild_and_force_reflow(&document, ReflowReason::CachedPageNeededReflow);
|
||||
document.window().thaw();
|
||||
document.fully_activate();
|
||||
return;
|
||||
}
|
||||
let mut loads = self.incomplete_loads.borrow_mut();
|
||||
if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
|
||||
load.is_frozen = false;
|
||||
return;
|
||||
}
|
||||
warn!("thaw sent to nonexistent pipeline");
|
||||
warn!("change of activity sent to nonexistent pipeline");
|
||||
}
|
||||
|
||||
fn handle_focus_iframe_msg(&self,
|
||||
|
@ -1759,16 +1741,13 @@ impl ScriptThread {
|
|||
is_html_document,
|
||||
content_type,
|
||||
last_modified,
|
||||
incomplete.activity,
|
||||
DocumentSource::FromParser,
|
||||
loader,
|
||||
referrer,
|
||||
referrer_policy);
|
||||
document.set_ready_state(DocumentReadyState::Loading);
|
||||
|
||||
if !incomplete.is_frozen {
|
||||
document.fully_activate();
|
||||
}
|
||||
|
||||
self.documents.borrow_mut().insert(incomplete.pipeline_id, &*document);
|
||||
|
||||
window.init_document(&document);
|
||||
|
@ -1822,8 +1801,8 @@ impl ScriptThread {
|
|||
ServoParser::parse_html_document(&document, parse_input, final_url);
|
||||
}
|
||||
|
||||
if incomplete.is_frozen {
|
||||
window.upcast::<GlobalScope>().suspend();
|
||||
if incomplete.activity != DocumentActivity::FullyActive {
|
||||
window.suspend();
|
||||
}
|
||||
|
||||
if !incomplete.is_visible {
|
||||
|
|
|
@ -233,6 +233,7 @@ impl OneshotTimers {
|
|||
return warn!("Suspending an already suspended timer.");
|
||||
}
|
||||
|
||||
debug!("Suspending timers.");
|
||||
self.suspended_since.set(Some(precise_time_ms()));
|
||||
self.invalidate_expected_event_id();
|
||||
}
|
||||
|
@ -244,6 +245,7 @@ impl OneshotTimers {
|
|||
None => return warn!("Resuming an already resumed timer."),
|
||||
};
|
||||
|
||||
debug!("Resuming timers.");
|
||||
self.suspension_offset.set(self.suspension_offset.get() + additional_offset);
|
||||
self.suspended_since.set(None);
|
||||
|
||||
|
@ -252,7 +254,7 @@ impl OneshotTimers {
|
|||
|
||||
fn schedule_timer_call(&self) {
|
||||
if self.suspended_since.get().is_some() {
|
||||
// The timer will be scheduled when the pipeline is thawed.
|
||||
// The timer will be scheduled when the pipeline is fully activated.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue