diff options
author | Alan Jeffrey <ajeffrey@mozilla.com> | 2016-11-30 16:54:12 -0600 |
---|---|---|
committer | Alan Jeffrey <ajeffrey@mozilla.com> | 2017-01-05 21:12:57 +0000 |
commit | 7c2de62124995c8c353756abdb1ec7c08973ae3a (patch) | |
tree | f6b54e6a181ee1498f0bf8fc06665f802078c5a0 /components/script/dom | |
parent | 143dfc879e609603839502d61bc064fba96cc80f (diff) | |
download | servo-7c2de62124995c8c353756abdb1ec7c08973ae3a.tar.gz servo-7c2de62124995c8c353756abdb1ec7c08973ae3a.zip |
Implement browsing context discarding.
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/browsingcontext.rs | 39 | ||||
-rw-r--r-- | components/script/dom/document.rs | 53 | ||||
-rw-r--r-- | components/script/dom/htmliframeelement.rs | 61 | ||||
-rw-r--r-- | components/script/dom/webidls/Window.webidl | 8 | ||||
-rw-r--r-- | components/script/dom/window.rs | 47 |
5 files changed, 96 insertions, 112 deletions
diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index 1a7f26253a0..9652445d96f 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -3,16 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject}; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, MutNullableJS, Root, RootedReference}; +use dom::bindings::js::{JS, Root, RootedReference}; use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor}; use dom::bindings::reflector::{DomObject, MutDomObject, Reflector}; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::WindowProxyHandler; use dom::bindings::utils::get_array_index_from_id; -use dom::document::Document; use dom::element::Element; -use dom::globalscope::GlobalScope; use dom::window::Window; use js::JSCLASS_IS_GLOBAL; use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy}; @@ -26,7 +23,6 @@ use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue}; use js::jsapi::{ObjectOpResult, PropertyDescriptor}; use js::jsval::{UndefinedValue, PrivateValue}; use js::rust::get_object_class; -use msg::constellation_msg::PipelineId; use std::cell::Cell; #[dom_struct] @@ -40,10 +36,8 @@ pub struct BrowsingContext { /// Indicates if reflow is required when reloading. needs_reflow: Cell<bool>, - /// The current active document. - /// Note that the session history is stored in the constellation, - /// in the script thread we just track the current active document. - active_document: MutNullableJS<Document>, + /// Has this browsing context been discarded? + discarded: Cell<bool>, /// The containing iframe element, if this is a same-origin iframe frame_element: Option<JS<Element>>, @@ -54,7 +48,7 @@ impl BrowsingContext { BrowsingContext { reflector: Reflector::new(), needs_reflow: Cell::new(true), - active_document: Default::default(), + discarded: Cell::new(false), frame_element: frame_element.map(JS::from_ref), } } @@ -84,20 +78,12 @@ impl BrowsingContext { } } - pub fn set_active_document(&self, document: &Document) { - self.active_document.set(Some(document)) + pub fn discard(&self) { + self.discarded.set(true); } - pub fn active_document(&self) -> Root<Document> { - self.active_document.get().expect("No active document.") - } - - pub fn maybe_active_document(&self) -> Option<Root<Document>> { - self.active_document.get() - } - - pub fn active_window(&self) -> Root<Window> { - Root::from_ref(self.active_document().window()) + pub fn is_discarded(&self) -> bool { + self.discarded.get() } pub fn frame_element(&self) -> Option<&Element> { @@ -115,15 +101,6 @@ impl BrowsingContext { self.needs_reflow.set(status); old } - - pub fn active_pipeline_id(&self) -> Option<PipelineId> { - self.active_document.get() - .map(|doc| doc.window().upcast::<GlobalScope>().pipeline_id()) - } - - pub fn unset_active_document(&self) { - self.active_document.set(None) - } } #[allow(unsafe_code)] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index d6d967db6c4..bfc98d61b0f 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -190,6 +190,7 @@ pub struct Document { last_modified: Option<String>, encoding: Cell<EncodingRef>, is_html_document: bool, + is_fully_active: Cell<bool>, url: DOMRefCell<ServoUrl>, quirks_mode: Cell<QuirksMode>, /// Caches for the getElement methods @@ -385,20 +386,17 @@ impl Document { self.trigger_mozbrowser_event(MozBrowserEvent::SecurityChange(https_state)); } - // https://html.spec.whatwg.org/multipage/#active-document - pub fn is_active(&self) -> bool { - self.browsing_context().map_or(false, |context| { - self == &*context.active_document() - }) - } - // https://html.spec.whatwg.org/multipage/#fully-active pub fn is_fully_active(&self) -> bool { - if !self.is_active() { - return false; - } - // FIXME: It should also check whether the browser context is top-level or not - true + 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) } pub fn origin(&self) -> &Origin { @@ -1693,11 +1691,16 @@ impl Document { self.current_parser.get() } - /// Find an iframe element in the document. - pub fn find_iframe(&self, frame_id: FrameId) -> Option<Root<HTMLIFrameElement>> { + /// Iterate over all iframes in the document. + pub fn iter_iframes(&self) -> impl Iterator<Item=Root<HTMLIFrameElement>> { self.upcast::<Node>() .traverse_preorder() .filter_map(Root::downcast::<HTMLIFrameElement>) + } + + /// Find an iframe element in the document. + pub fn find_iframe(&self, frame_id: FrameId) -> Option<Root<HTMLIFrameElement>> { + self.iter_iframes() .find(|node| node.frame_id() == frame_id) } @@ -1864,6 +1867,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), id_map: DOMRefCell::new(HashMap::new()), tag_map: DOMRefCell::new(HashMap::new()), tagns_map: DOMRefCell::new(HashMap::new()), @@ -2261,19 +2265,12 @@ impl DocumentMethods for Document { // https://html.spec.whatwg.org/multipage/#dom-document-hasfocus fn HasFocus(&self) -> bool { - match self.browsing_context() { - Some(browsing_context) => { - // Step 2. - let candidate = browsing_context.active_document(); - // Step 3. - if &*candidate == self { - true - } else { - false //TODO Step 4. - } - } - None => false, + // Step 1-2. + if self.window().parent_info().is_none() && self.is_fully_active() { + return true; } + // TODO Step 3. + false } // https://html.spec.whatwg.org/multipage/#relaxing-the-same-origin-restriction @@ -3190,8 +3187,8 @@ impl DocumentMethods for Document { // Step 2. // TODO: handle throw-on-dynamic-markup-insertion counter. - - if !self.is_active() { + // FIXME: this should check for being active rather than fully active + if !self.is_fully_active() { // Step 3. return Ok(()); } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 692598d5339..3a1c9daf1c9 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -33,7 +33,6 @@ use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; use dom::htmlelement::HTMLElement; use dom::node::{Node, NodeDamage, UnbindContext, document_from_node, window_from_node}; -use dom::urlhelper::UrlHelper; use dom::virtualmethods::VirtualMethods; use dom::window::{ReflowReason, Window}; use html5ever_atoms::LocalName; @@ -709,42 +708,34 @@ impl VirtualMethods for HTMLIFrameElement { LoadBlocker::terminate(&mut blocker); // https://html.spec.whatwg.org/multipage/#a-browsing-context-is-discarded - if let Some(pipeline_id) = self.pipeline_id.get() { - debug!("Unbinding pipeline {} from frame {}.", pipeline_id, self.frame_id); - let window = window_from_node(self); - - // The only reason we're waiting for the iframe to be totally - // removed is to ensure the script thread can't add iframes faster - // than the compositor can remove them. - // - // Since most of this cleanup doesn't happen on same-origin - // iframes, and since that would cause a deadlock, don't do it. - let same_origin = { - // FIXME(#10968): this should probably match the origin check in - // HTMLIFrameElement::contentDocument. - let self_url = self.get_url(); - let win_url = window_from_node(self).get_url(); - UrlHelper::SameOrigin(&self_url, &win_url) || self_url.as_str() == "about:blank" - }; - let (sender, receiver) = if same_origin { - (None, None) - } else { - let (sender, receiver) = ipc::channel().unwrap(); - (Some(sender), Some(receiver)) - }; - let msg = ConstellationMsg::RemoveIFrame(pipeline_id, sender); - window.upcast::<GlobalScope>().constellation_chan().send(msg).unwrap(); - if let Some(receiver) = receiver { - receiver.recv().unwrap() + debug!("Unbinding frame {}.", self.frame_id); + let window = window_from_node(self); + let (sender, receiver) = ipc::channel().unwrap(); + + // Ask the constellation to remove the iframe, and tell us the + // pipeline ids of the closed pipelines. + let msg = ConstellationMsg::RemoveIFrame(self.frame_id, sender); + window.upcast::<GlobalScope>().constellation_chan().send(msg).unwrap(); + let exited_pipeline_ids = receiver.recv().unwrap(); + + // The spec for discarding is synchronous, + // so we need to discard the browsing contexts now, rather than + // when the `PipelineExit` message arrives. + for exited_pipeline_id in exited_pipeline_ids { + if let Some(exited_document) = ScriptThread::find_document(exited_pipeline_id) { + exited_document.window().browsing_context().discard(); + for exited_iframe in exited_document.iter_iframes() { + exited_iframe.pipeline_id.set(None); + } } - - // Resetting the pipeline_id to None is required here so that - // if this iframe is subsequently re-added to the document - // the load doesn't think that it's a navigation, but instead - // a new iframe. Without this, the constellation gets very - // confused. - self.pipeline_id.set(None); } + + // Resetting the pipeline_id to None is required here so that + // if this iframe is subsequently re-added to the document + // the load doesn't think that it's a navigation, but instead + // a new iframe. Without this, the constellation gets very + // confused. + self.pipeline_id.set(None); } } diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index dbc4b76591e..47c753f43b1 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -31,9 +31,13 @@ // other browsing contexts [Replaceable] readonly attribute WindowProxy frames; //[Replaceable] readonly attribute unsigned long length; - [Unforgeable] readonly attribute WindowProxy top; + // Note that this can return null in the case that the browsing context has been discarded. + // https://github.com/whatwg/html/issues/2115 + [Unforgeable] readonly attribute WindowProxy? top; // attribute any opener; - readonly attribute WindowProxy parent; + // Note that this can return null in the case that the browsing context has been discarded. + // https://github.com/whatwg/html/issues/2115 + readonly attribute WindowProxy? parent; readonly attribute Element? frameElement; //WindowProxy open(optional DOMString url = "about:blank", optional DOMString target = "_blank", // optional DOMString features = "", optional boolean replace = false); diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 37bf817ed7a..87d15ab1616 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -163,6 +163,7 @@ pub struct Window { #[ignore_heap_size_of = "channels are hard"] image_cache_chan: ImageCacheChan, browsing_context: MutNullableJS<BrowsingContext>, + document: MutNullableJS<Document>, history: MutNullableJS<History>, performance: MutNullableJS<Performance>, navigation_start: u64, @@ -443,7 +444,7 @@ impl WindowMethods for Window { // https://html.spec.whatwg.org/multipage/#dom-document-2 fn Document(&self) -> Root<Document> { - self.browsing_context().active_document() + self.document.get().expect("Document accessed before initialization.") } // https://html.spec.whatwg.org/multipage/#dom-history @@ -551,21 +552,32 @@ impl WindowMethods for Window { } // https://html.spec.whatwg.org/multipage/#dom-parent - fn Parent(&self) -> Root<BrowsingContext> { - match self.parent() { - Some(window) => window.browsing_context(), - None => self.Window() + fn GetParent(&self) -> Option<Root<BrowsingContext>> { + // Steps 1. and 2. + if self.browsing_context().is_discarded() { + return None; } - } + match self.parent() { + // Step 4. + Some(parent) => Some(parent.Window()), + // Step 5. + None => Some(self.Window()) + } + } // https://html.spec.whatwg.org/multipage/#dom-top - fn Top(&self) -> Root<BrowsingContext> { + fn GetTop(&self) -> Option<Root<BrowsingContext>> { + // Steps 1. and 2. + if self.browsing_context().is_discarded() { + return None; + } + // Step 5. let mut window = Root::from_ref(self); while let Some(parent) = window.parent() { window = parent; } - window.browsing_context() - } + Some(window.Window()) + } // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/ // NavigationTiming/Overview.html#sec-window.performance-attribute @@ -1351,6 +1363,13 @@ impl Window { unsafe { SetWindowProxy(cx, window, browsing_context.reflector().get_jsobject()); } } + #[allow(unsafe_code)] + pub fn init_document(&self, document: &Document) { + assert!(self.document.get().is_none()); + assert!(document.window() == self); + self.document.set(Some(&document)); + } + /// Commence a new URL load which will either replace this window or scroll to a fragment. pub fn load_url(&self, url: ServoUrl, replace: bool, force_reload: bool, referrer_policy: Option<ReferrerPolicy>) { @@ -1518,13 +1537,8 @@ impl Window { return None; } - let browsing_context = self.browsing_context(); - - browsing_context.frame_element().map(|frame_element| { - let window = window_from_node(frame_element); - let context = window.browsing_context(); - context.active_window() - }) + self.browsing_context().frame_element() + .map(|frame_element| window_from_node(frame_element)) } /// Returns whether this window is mozbrowser. @@ -1610,6 +1624,7 @@ impl Window { image_cache_thread: image_cache_thread, history: Default::default(), browsing_context: Default::default(), + document: Default::default(), performance: Default::default(), navigation_start: (current_time.sec * 1000 + current_time.nsec as i64 / 1000000) as u64, navigation_start_precise: time::precise_time_ns() as f64, |