diff options
Diffstat (limited to 'components/script/script_thread.rs')
-rw-r--r-- | components/script/script_thread.rs | 222 |
1 files changed, 74 insertions, 148 deletions
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 57cdd7044c4..e1e07fd177e 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -41,7 +41,7 @@ use dom::bindings::str::DOMString; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::WRAP_CALLBACKS; use dom::browsingcontext::BrowsingContext; -use dom::document::{Document, DocumentSource, FocusType, IsHTMLDocument, TouchEventResult}; +use dom::document::{Document, DocumentSource, FocusType, HasBrowsingContext, IsHTMLDocument, TouchEventResult}; use dom::element::Element; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::globalscope::GlobalScope; @@ -57,7 +57,7 @@ use dom::window::{ReflowReason, Window}; use dom::worker::TrustedWorkerAddress; use euclid::Rect; use euclid::point::Point2D; -use hyper::header::{ContentType, HttpDate, LastModified}; +use hyper::header::{ContentType, HttpDate, LastModified, Headers}; use hyper::header::ReferrerPolicy as ReferrerPolicyHeader; use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper_serde::Serde; @@ -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}; @@ -97,6 +98,7 @@ use servo_config::opts; use servo_url::ServoUrl; use std::cell::Cell; use std::collections::{hash_map, HashMap, HashSet}; +use std::ops::Deref; use std::option::Option; use std::ptr; use std::rc::Rc; @@ -147,10 +149,8 @@ struct InProgressLoad { window_size: Option<WindowSizeData>, /// Channel to the layout thread associated with this pipeline. layout_chan: Sender<message::Msg>, - /// The current viewport clipping rectangle applying to this pipeline, if any. - clip_rect: Option<Rect<f32>>, - /// 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. @@ -173,8 +173,7 @@ impl InProgressLoad { parent_info: parent_info, layout_chan: layout_chan, window_size: window_size, - clip_rect: None, - is_frozen: false, + activity: DocumentActivity::FullyActive, is_visible: true, url: url, origin: origin, @@ -392,13 +391,15 @@ impl<'a> Iterator for DocumentsIter<'a> { } } - #[derive(JSTraceable)] // ScriptThread instances are rooted on creation, so this is okay #[allow(unrooted_must_root)] pub struct ScriptThread { /// The documents for pipelines managed by this thread documents: DOMRefCell<Documents>, + /// The browsing contexts known by this thread + /// TODO: this map grows, but never shrinks. Issue #15258. + browsing_contexts: DOMRefCell<HashMap<FrameId, JS<BrowsingContext>>>, /// A list of data pertaining to loads that have not yet received a network response incomplete_loads: DOMRefCell<Vec<InProgressLoad>>, /// A map to store service worker registrations for a given origin @@ -652,6 +653,7 @@ impl ScriptThread { ScriptThread { documents: DOMRefCell::new(Documents::new()), + browsing_contexts: DOMRefCell::new(HashMap::new()), incomplete_loads: DOMRefCell::new(vec!()), registration_map: DOMRefCell::new(HashMap::new()), job_queue_map: Rc::new(JobQueue::new()), @@ -965,10 +967,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) => @@ -1166,9 +1166,8 @@ impl ScriptThread { } 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.clip_rect = Some(rect); + let loads = self.incomplete_loads.borrow(); + if loads.iter().any(|load| load.pipeline_id == id) { return; } warn!("Page rect message sent to nonexistent pipeline"); @@ -1274,21 +1273,6 @@ impl ScriptThread { reports_chan.send(reports); } - /// To slow/speed up timers and manage any other script thread resource based on visibility. - /// Returns true if successful. - fn alter_resource_utilization(&self, id: PipelineId, visible: bool) -> bool { - let window = self.documents.borrow().find_window(id); - if let Some(window) = window { - if visible { - window.upcast::<GlobalScope>().speed_up_timers(); - } else { - window.upcast::<GlobalScope>().slow_down_timers(); - } - return true; - } - false - } - /// Updates iframe element after a change in visibility fn handle_visibility_change_complete_msg(&self, parent_pipeline_id: PipelineId, id: FrameId, visible: bool) { let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, id); @@ -1299,56 +1283,42 @@ impl ScriptThread { /// Handle visibility change message fn handle_visibility_change_msg(&self, id: PipelineId, visible: bool) { - let resources_altered = self.alter_resource_utilization(id, visible); - // Separate message sent since parent script thread could be different (Iframe of different // domain) self.constellation_chan.send(ConstellationMsg::VisibilityChangeComplete(id, visible)).unwrap(); - if !resources_altered { - 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_visible = visible; + let window = self.documents.borrow().find_window(id); + match window { + Some(window) => { + window.alter_resource_utilization(visible); return; } - } else { - return; + None => { + 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_visible = visible; + return; + } + } } warn!("change visibility message sent to nonexistent pipeline"); } - /// Handles freeze message - fn handle_freeze_msg(&self, id: PipelineId) { - let document = self.documents.borrow().find_document(id); - if let Some(document) = document { - document.window().freeze(); - document.fully_deactivate(); - 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; - return; - } - warn!("freeze sent to nonexistent pipeline"); - } - - /// Handles thaw message - fn handle_thaw_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 { - self.rebuild_and_force_reflow(&document, ReflowReason::CachedPageNeededReflow); - document.window().thaw(); - document.fully_activate(); + 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 = false; + load.activity = activity; return; } - warn!("thaw sent to nonexistent pipeline"); + warn!("change of activity sent to nonexistent pipeline"); } fn handle_focus_iframe_msg(&self, @@ -1547,29 +1517,26 @@ impl ScriptThread { load.pipeline_id == id }); - if let Some(idx) = idx { + let chan = if let Some(idx) = idx { let load = self.incomplete_loads.borrow_mut().remove(idx); - - // Tell the layout thread to begin shutting down, and wait until it - // processed this message. - let (response_chan, response_port) = channel(); - let chan = &load.layout_chan; - if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() { - debug!("shutting down layout for page {}", id); - response_port.recv().unwrap(); - chan.send(message::Msg::ExitNow).ok(); - } - } - - if let Some(document) = self.documents.borrow_mut().remove(id) { - shut_down_layout(document.window()); + load.layout_chan.clone() + } else if let Some(document) = self.documents.borrow_mut().remove(id) { + let window = document.window(); if discard_bc == DiscardBrowsingContext::Yes { - if let Some(context) = document.browsing_context() { - context.discard(); - } + window.browsing_context().discard(); } - let _ = self.constellation_chan.send(ConstellationMsg::PipelineExited(id)); - } + window.clear_js_runtime(); + window.layout_chan().clone() + } else { + return warn!("Exiting nonexistant pipeline {}.", id); + }; + + let (response_chan, response_port) = channel(); + chan.send(message::Msg::PrepareToExit(response_chan)).ok(); + debug!("shutting down layout for page {}", id); + response_port.recv().unwrap(); + chan.send(message::Msg::ExitNow).ok(); + self.constellation_chan.send(ConstellationMsg::PipelineExited(id)).ok(); debug!("Exited pipeline {}.", id); } @@ -1726,8 +1693,18 @@ impl ScriptThread { self.webvr_thread.clone()); let frame_element = frame_element.r().map(Castable::upcast); - let browsing_context = BrowsingContext::new(&window, frame_element); - window.init_browsing_context(&browsing_context); + match self.browsing_contexts.borrow_mut().entry(incomplete.frame_id) { + hash_map::Entry::Vacant(entry) => { + let browsing_context = BrowsingContext::new(&window, frame_element); + entry.insert(JS::from_ref(&*browsing_context)); + window.init_browsing_context(&browsing_context); + }, + hash_map::Entry::Occupied(entry) => { + let browsing_context = entry.get(); + browsing_context.set_window_proxy(&window); + window.init_browsing_context(browsing_context); + }, + } let last_modified = metadata.headers.as_ref().and_then(|headers| { headers.get().map(|&LastModified(HttpDate(ref tm))| dom_last_modified(tm)) @@ -1761,46 +1738,26 @@ impl ScriptThread { None => None, }; - let referrer_policy = if let Some(headers) = metadata.headers { - headers.get::<ReferrerPolicyHeader>().map(|h| match *h { - ReferrerPolicyHeader::NoReferrer => - ReferrerPolicy::NoReferrer, - ReferrerPolicyHeader::NoReferrerWhenDowngrade => - ReferrerPolicy::NoReferrerWhenDowngrade, - ReferrerPolicyHeader::SameOrigin => - ReferrerPolicy::SameOrigin, - ReferrerPolicyHeader::Origin => - ReferrerPolicy::Origin, - ReferrerPolicyHeader::OriginWhenCrossOrigin => - ReferrerPolicy::OriginWhenCrossOrigin, - ReferrerPolicyHeader::UnsafeUrl => - ReferrerPolicy::UnsafeUrl, - ReferrerPolicyHeader::StrictOrigin => - ReferrerPolicy::StrictOrigin, - ReferrerPolicyHeader::StrictOriginWhenCrossOrigin => - ReferrerPolicy::StrictOriginWhenCrossOrigin, - }) - } else { - None - }; + let referrer_policy = metadata.headers + .as_ref() + .map(Serde::deref) + .and_then(Headers::get::<ReferrerPolicyHeader>) + .map(ReferrerPolicy::from); let document = Document::new(&window, - Some(&browsing_context), + HasBrowsingContext::Yes, Some(final_url.clone()), incomplete.origin, 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); @@ -1849,25 +1806,17 @@ impl ScriptThread { document.set_https_state(metadata.https_state); if is_html_document == IsHTMLDocument::NonHTMLDocument { - ServoParser::parse_xml_document( - &document, - parse_input, - final_url, - Some(incomplete.pipeline_id)); + ServoParser::parse_xml_document(&document, parse_input, final_url); } else { - ServoParser::parse_html_document( - &document, - parse_input, - final_url, - Some(incomplete.pipeline_id)); + 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 { - self.alter_resource_utilization(incomplete.pipeline_id, false); + window.alter_resource_utilization(false); } document.get_current_parser().unwrap() @@ -2200,29 +2149,6 @@ impl Drop for ScriptThread { } } -/// Shuts down layout for the given window. -fn shut_down_layout(window: &Window) { - // Tell the layout thread to begin shutting down, and wait until it - // processed this message. - let (response_chan, response_port) = channel(); - let chan = window.layout_chan().clone(); - if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() { - let _ = response_port.recv(); - } - - // The browsing context is cleared by window.clear_js_runtime(), so we need to save a copy - let browsing_context = window.browsing_context(); - - // Drop our references to the JSContext and DOM objects. - window.clear_js_runtime(); - - // Discard the browsing context. - browsing_context.discard(); - - // Destroy the layout thread. If there were node leaks, layout will now crash safely. - chan.send(message::Msg::ExitNow).ok(); -} - fn dom_last_modified(tm: &Tm) -> String { tm.to_local().strftime("%m/%d/%Y %H:%M:%S").unwrap().to_string() } |