aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-01-27 16:30:37 -0800
committerGitHub <noreply@github.com>2017-01-27 16:30:37 -0800
commitb5c94bad371114ab9f03e910f66c00a042997fc2 (patch)
tree46aec036b4e1681f7d22762817b3afd3a615a7e0
parentd4ee8a3599a57078735766640b31df31c67d8201 (diff)
parentca9cee084e5ee8282c5d074f763b23936efc394f (diff)
downloadservo-b5c94bad371114ab9f03e910f66c00a042997fc2.tar.gz
servo-b5c94bad371114ab9f03e910f66c00a042997fc2.zip
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 -->
-rw-r--r--components/constellation/constellation.rs87
-rw-r--r--components/constellation/pipeline.rs19
-rw-r--r--components/script/dom/bindings/trace.rs4
-rw-r--r--components/script/dom/document.rs51
-rw-r--r--components/script/dom/domimplementation.rs3
-rw-r--r--components/script/dom/domparser.rs3
-rw-r--r--components/script/dom/node.rs4
-rw-r--r--components/script/dom/servoparser/mod.rs2
-rw-r--r--components/script/dom/window.rs10
-rw-r--r--components/script/dom/xmldocument.rs5
-rw-r--r--components/script/dom/xmlhttprequest.rs2
-rw-r--r--components/script/script_thread.rs53
-rw-r--r--components/script/timers.rs4
-rw-r--r--components/script_traits/lib.rs25
-rw-r--r--tests/wpt/metadata/MANIFEST.json6
-rw-r--r--tests/wpt/metadata/html/dom/dynamic-markup-insertion/document-write/write-active-document.html.ini4
-rw-r--r--tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/empty.html1
-rw-r--r--tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/write-active-document.html35
18 files changed, 214 insertions, 104 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index 132bf40e1ac..621e92fb6b1 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -95,7 +95,7 @@ use profile_traits::mem;
use profile_traits::time;
use script_traits::{AnimationState, AnimationTickType, CompositorEvent};
use script_traits::{ConstellationControlMsg, ConstellationMsg as FromCompositorMsg, DiscardBrowsingContext};
-use script_traits::{DocumentState, LayoutControlMsg, LoadData};
+use script_traits::{DocumentActivity, DocumentState, LayoutControlMsg, LoadData};
use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, TimerEventRequest};
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{LogEntry, ServiceWorkerMsg, webdriver_msg};
@@ -1405,10 +1405,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let window_size = old_pipeline.and_then(|old_pipeline| old_pipeline.size);
- if let Some(old_pipeline) = old_pipeline {
- old_pipeline.freeze();
- }
-
(load_data, window_size, is_private)
};
@@ -1628,11 +1624,6 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
});
self.new_pipeline(new_pipeline_id, root_frame_id, None, window_size, load_data, sandbox, false);
- // Send message to ScriptThread that will suspend all timers
- match self.pipelines.get(&source_id) {
- Some(source) => source.freeze(),
- None => warn!("Pipeline {:?} loaded after closure", source_id),
- };
Some(new_pipeline_id)
}
}
@@ -2050,13 +2041,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.focus_pipeline_id = Some(pipeline_id);
}
- // Suspend the old pipeline, and resume the new one.
- if let Some(pipeline) = self.pipelines.get(&old_pipeline_id) {
- pipeline.freeze();
- }
- if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
- pipeline.thaw();
- }
+ // Deactivate the old pipeline, and activate the new one.
+ self.update_activity(old_pipeline_id);
+ self.update_activity(pipeline_id);
// Set paint permissions correctly for the compositor layers.
self.send_frame_tree();
@@ -2125,22 +2112,24 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
}
- let (evicted_id, new_frame, clear_future, location_changed) = if let Some(mut entry) = frame_change.replace {
+ let (evicted_id, new_frame, navigated, location_changed) = if let Some(mut entry) = frame_change.replace {
debug!("Replacing pipeline in existing frame.");
let evicted_id = entry.pipeline_id;
entry.replace_pipeline(frame_change.new_pipeline_id, frame_change.url.clone());
self.traverse_to_entry(entry);
- (evicted_id, false, false, false)
+ (evicted_id, false, None, false)
} else if let Some(frame) = self.frames.get_mut(&frame_change.frame_id) {
debug!("Adding pipeline to existing frame.");
+ let old_pipeline_id = frame.pipeline_id;
frame.load(frame_change.new_pipeline_id, frame_change.url.clone());
let evicted_id = frame.prev.len()
.checked_sub(PREFS.get("session-history.max-length").as_u64().unwrap_or(20) as usize)
.and_then(|index| frame.prev.get_mut(index))
.and_then(|entry| entry.pipeline_id.take());
- (evicted_id, false, true, true)
+ (evicted_id, false, Some(old_pipeline_id), true)
} else {
- (None, true, false, true)
+ debug!("Adding pipeline to new frame.");
+ (None, true, None, true)
};
if let Some(evicted_id) = evicted_id {
@@ -2149,9 +2138,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
if new_frame {
self.new_frame(frame_change.frame_id, frame_change.new_pipeline_id, frame_change.url);
+ self.update_activity(frame_change.new_pipeline_id);
};
- if clear_future {
+ if let Some(old_pipeline_id) = navigated {
+ // Deactivate the old pipeline, and activate the new one.
+ self.update_activity(old_pipeline_id);
+ self.update_activity(frame_change.new_pipeline_id);
+ // Clear the joint session future
let top_level_frame_id = self.get_top_level_frame_for_pipeline(frame_change.new_pipeline_id);
self.clear_joint_session_future(top_level_frame_id);
}
@@ -2165,7 +2159,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
fn handle_activate_document_msg(&mut self, pipeline_id: PipelineId) {
- debug!("Document ready to activate {:?}", pipeline_id);
+ debug!("Document ready to activate {}", pipeline_id);
// Notify the parent (if there is one).
if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
@@ -2359,6 +2353,53 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
ReadyToSave::Ready
}
+ /// Get the current activity of a pipeline.
+ fn get_activity(&self, pipeline_id: PipelineId) -> DocumentActivity {
+ let mut ancestor_id = pipeline_id;
+ loop {
+ if let Some(ancestor) = self.pipelines.get(&ancestor_id) {
+ if let Some(frame) = self.frames.get(&ancestor.frame_id) {
+ if frame.pipeline_id == ancestor_id {
+ if let Some((parent_id, FrameType::IFrame)) = ancestor.parent_info {
+ ancestor_id = parent_id;
+ continue;
+ } else {
+ return DocumentActivity::FullyActive;
+ }
+ }
+ }
+ }
+ if pipeline_id == ancestor_id {
+ return DocumentActivity::Inactive;
+ } else {
+ return DocumentActivity::Active;
+ }
+ }
+ }
+
+ /// Set the current activity of a pipeline.
+ fn set_activity(&self, pipeline_id: PipelineId, activity: DocumentActivity) {
+ debug!("Setting activity of {} to be {:?}.", pipeline_id, activity);
+ if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
+ pipeline.set_activity(activity);
+ let child_activity = if activity == DocumentActivity::Inactive {
+ DocumentActivity::Active
+ } else {
+ activity
+ };
+ for child_id in &pipeline.children {
+ if let Some(child) = self.frames.get(child_id) {
+ self.set_activity(child.pipeline_id, child_activity);
+ }
+ }
+ }
+ }
+
+ /// Update the current activity of a pipeline.
+ fn update_activity(&self, pipeline_id: PipelineId) {
+ self.set_activity(pipeline_id, self.get_activity(pipeline_id));
+ }
+
fn clear_joint_session_future(&mut self, frame_id: FrameId) {
let frame_ids: Vec<FrameId> = self.full_frame_tree_iter(frame_id)
.map(|frame| frame.id)
diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs
index 075c53e84f8..e8a6ab3c05b 100644
--- a/components/constellation/pipeline.rs
+++ b/components/constellation/pipeline.rs
@@ -19,7 +19,8 @@ use net_traits::{IpcSend, ResourceThreads};
use net_traits::image_cache_thread::ImageCacheThread;
use profile_traits::mem as profile_mem;
use profile_traits::time;
-use script_traits::{ConstellationControlMsg, DevicePixel, DiscardBrowsingContext, InitialScriptState};
+use script_traits::{ConstellationControlMsg, DevicePixel, DiscardBrowsingContext};
+use script_traits::{DocumentActivity, InitialScriptState};
use script_traits::{LayoutControlMsg, LayoutMsg, LoadData, MozBrowserEvent};
use script_traits::{NewLayoutInfo, SWManagerMsg, SWManagerSenders, ScriptMsg};
use script_traits::{ScriptThreadFactory, TimerEventRequest, WindowSizeData};
@@ -366,17 +367,11 @@ impl Pipeline {
}
}
- /// Notify this pipeline that it is no longer fully active.
- pub fn freeze(&self) {
- if let Err(e) = self.event_loop.send(ConstellationControlMsg::Freeze(self.id)) {
- warn!("Sending freeze message failed ({}).", e);
- }
- }
-
- /// Notify this pipeline that it is fully active.
- pub fn thaw(&self) {
- if let Err(e) = self.event_loop.send(ConstellationControlMsg::Thaw(self.id)) {
- warn!("Sending freeze message failed ({}).", e);
+ /// Notify this pipeline of its activity.
+ pub fn set_activity(&self, activity: DocumentActivity) {
+ let msg = ConstellationControlMsg::SetDocumentActivity(self.id, activity);
+ if let Err(e) = self.event_loop.send(msg) {
+ warn!("Sending activity message failed ({}).", e);
}
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 6d268823d77..6e0ce0d47dc 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -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);
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 53aa4af882d..da4d715fe21 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -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()
- }
-
- pub fn fully_activate(&self) {
- self.is_fully_active.set(true)
- }
-
- pub fn fully_deactivate(&self) {
- self.is_fully_active.set(false)
+ self.activity.get() == DocumentActivity::FullyActive
+ }
+
+ pub fn is_active(&self) -> bool {
+ self.activity.get() != DocumentActivity::Inactive
+ }
+
+ 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(());
}
diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs
index 23521b94a09..45983c3292d 100644
--- a/components/script/dom/domimplementation.rs
+++ b/components/script/dom/domimplementation.rs
@@ -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,
diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs
index c14bec8b594..54132ef94a9 100644
--- a/components/script/dom/domparser.rs
+++ b/components/script/dom/domparser.rs
@@ -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,
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 71547c860bc..6931fca1822 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -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)
},
diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs
index aaa36a2098b..d64704b2368 100644
--- a/components/script/dom/servoparser/mod.rs
+++ b/components/script/dom/servoparser/mod.rs
@@ -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,
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index cb9d5abdf07..37c62543526 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -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 {
diff --git a/components/script/dom/xmldocument.rs b/components/script/dom/xmldocument.rs
index 87c639742f6..593749c4499 100644
--- a/components/script/dom/xmldocument.rs
+++ b/components/script/dom/xmldocument.rs
@@ -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,
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index 95a8ba0c3ad..f42afe87f8b 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -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,
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 9bab1679aa6..2bdb75a486f 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -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 {
diff --git a/components/script/timers.rs b/components/script/timers.rs
index 881fe9b6034..c1f0a1de6c4 100644
--- a/components/script/timers.rs
+++ b/components/script/timers.rs
@@ -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;
}
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 295000380ca..5be4cb7d2bc 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -194,6 +194,22 @@ pub enum DiscardBrowsingContext {
No,
}
+/// Is a document fully active, active or inactive?
+/// A document is active if it is the current active document in its session history,
+/// it is fuly active if it is active and all of its ancestors are active,
+/// and it is inactive otherwise.
+/// https://html.spec.whatwg.org/multipage/#active-document
+/// https://html.spec.whatwg.org/multipage/#fully-active
+#[derive(Copy, Clone, PartialEq, Eq, Hash, HeapSizeOf, Debug, Deserialize, Serialize)]
+pub enum DocumentActivity {
+ /// An inactive document
+ Inactive,
+ /// An active but not fully active document
+ Active,
+ /// A fully active document
+ FullyActive,
+}
+
/// Messages sent from the constellation or layout to the script thread.
#[derive(Deserialize, Serialize)]
pub enum ConstellationControlMsg {
@@ -215,10 +231,8 @@ pub enum ConstellationControlMsg {
SetScrollState(PipelineId, Vec<(UntrustedNodeAddress, Point2D<f32>)>),
/// Requests that the script thread immediately send the constellation the title of a pipeline.
GetTitle(PipelineId),
- /// Notifies script thread to suspend all its timers
- Freeze(PipelineId),
- /// Notifies script thread to resume all its timers
- Thaw(PipelineId),
+ /// Notifies script thread of a change to one of its document's activity
+ SetDocumentActivity(PipelineId, DocumentActivity),
/// Notifies script thread whether frame is visible
ChangeFrameVisibilityStatus(PipelineId, bool),
/// Notifies script thread that frame visibility change is complete
@@ -281,8 +295,7 @@ impl fmt::Debug for ConstellationControlMsg {
Viewport(..) => "Viewport",
SetScrollState(..) => "SetScrollState",
GetTitle(..) => "GetTitle",
- Freeze(..) => "Freeze",
- Thaw(..) => "Thaw",
+ SetDocumentActivity(..) => "SetDocumentActivity",
ChangeFrameVisibilityStatus(..) => "ChangeFrameVisibilityStatus",
NotifyVisibilityChange(..) => "NotifyVisibilityChange",
Navigate(..) => "Navigate",
diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json
index 82c13ee87e5..51d045d6c52 100644
--- a/tests/wpt/metadata/MANIFEST.json
+++ b/tests/wpt/metadata/MANIFEST.json
@@ -45873,6 +45873,12 @@
"url": "/cssom/stylesheet-same-origin.sub.html"
}
],
+ "html/dom/dynamic-markup-insertion/document-write/write-active-document.html": [
+ {
+ "path": "html/dom/dynamic-markup-insertion/document-write/write-active-document.html",
+ "url": "/html/dom/dynamic-markup-insertion/document-write/write-active-document.html"
+ }
+ ],
"html/semantics/embedded-content/the-iframe-element/iframe-synchronously-discard.html": [
{
"path": "html/semantics/embedded-content/the-iframe-element/iframe-synchronously-discard.html",
diff --git a/tests/wpt/metadata/html/dom/dynamic-markup-insertion/document-write/write-active-document.html.ini b/tests/wpt/metadata/html/dom/dynamic-markup-insertion/document-write/write-active-document.html.ini
new file mode 100644
index 00000000000..09620a4a26b
--- /dev/null
+++ b/tests/wpt/metadata/html/dom/dynamic-markup-insertion/document-write/write-active-document.html.ini
@@ -0,0 +1,4 @@
+[write-active-document.html]
+ type: testharness
+ [document.write only writes to active documents]
+ expected: FAIL
diff --git a/tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/empty.html b/tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/empty.html
new file mode 100644
index 00000000000..0dc101b5335
--- /dev/null
+++ b/tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/empty.html
@@ -0,0 +1 @@
+<html><body></body></html>
diff --git a/tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/write-active-document.html b/tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/write-active-document.html
new file mode 100644
index 00000000000..6faffd81de4
--- /dev/null
+++ b/tests/wpt/web-platform-tests/html/dom/dynamic-markup-insertion/document-write/write-active-document.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>document.write only writes to active documents</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body><div id="log"></div></body>
+<script>
+ async_test(function(t) {
+ var child = document.createElement("iframe");
+ child.src = "empty.html?1";
+ child.onload = t.step_func(function() {
+ var child1 = child.contentDocument;
+ var link = child1.createElement("a");
+ link.href = "data:text/html,Clicked.";
+ link.innerText = "Link.";
+ child1.body.appendChild(link);
+ var grandchild = child1.createElement("iframe");
+ grandchild.src = "empty.html?2";
+ grandchild.onload = t.step_func(function() {
+ var grandchild1 = grandchild.contentDocument;
+ child.onload = t.step_func(function() {
+ // This is a write to an inactive document
+ child1.write('WRITE HAPPENED');
+ assert_equals(child1.body.lastChild.tagName, "IFRAME");
+ // This is a write to an active but not fully active document
+ grandchild1.write('WRITE HAPPENED');
+ assert_equals(grandchild1.body.innerHTML, "WRITE HAPPENED");
+ t.done();
+ });
+ link.click();
+ });
+ child1.body.appendChild(grandchild);
+ });
+ document.body.appendChild(child);
+ });
+</script>