diff options
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> |