diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index 1c2ca66b33e1..9f0be02e4842 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -5,7 +5,7 @@ use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject}; 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::reflector::{DomObject, Reflector}; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::WindowProxyHandler; use dom::bindings::utils::get_array_index_from_id; @@ -19,11 +19,13 @@ use js::jsapi::{JSAutoCompartment, JSContext, JSErrNum, JSFreeOp, JSObject}; use js::jsapi::{JSPROP_READONLY, JSTracer, JS_DefinePropertyById}; use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo}; use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById}; +use js::jsapi::{JS_TransplantObject, SetWindowProxy}; use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue}; use js::jsapi::{ObjectOpResult, PropertyDescriptor}; use js::jsval::{UndefinedValue, PrivateValue}; use js::rust::get_object_class; use std::cell::Cell; +use std::ptr; #[dom_struct] // NOTE: the browsing context for a window is managed in two places: @@ -31,6 +33,10 @@ use std::cell::Cell; // manages the session history, which in script is accessed through // History objects, messaging the constellation. pub struct BrowsingContext { + /// The WindowProxy object. + /// Unlike other reflectors, we mutate this field because + /// we have to brain-transplant the reflector when the WindowProxy + /// changes Window. reflector: Reflector, /// Has this browsing context been discarded? @@ -44,7 +50,7 @@ impl BrowsingContext { pub fn new_inherited(frame_element: Option<&Element>) -> BrowsingContext { BrowsingContext { reflector: Reflector::new(), - discarded: Cell::new(false), + discarded: Cell::new(false), frame_element: frame_element.map(JS::from_ref), } } @@ -56,21 +62,29 @@ impl BrowsingContext { assert!(!handler.is_null()); let cx = window.get_cx(); - let parent = window.reflector().get_jsobject(); - assert!(!parent.get().is_null()); - assert!(((*get_object_class(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0); - let _ac = JSAutoCompartment::new(cx, parent.get()); - rooted!(in(cx) let window_proxy = NewWindowProxy(cx, parent, handler)); + let window_jsobject = window.reflector().get_jsobject(); + assert!(!window_jsobject.get().is_null()); + assert!(((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL) != 0); + let _ac = JSAutoCompartment::new(cx, window_jsobject.get()); + + // Create a new window proxy. + rooted!(in(cx) let window_proxy = NewWindowProxy(cx, window_jsobject, handler)); assert!(!window_proxy.is_null()); - let object = box BrowsingContext::new_inherited(frame_element); + // Create a new browsing context. + let mut browsing_context = box BrowsingContext::new_inherited(frame_element); - let raw = Box::into_raw(object); - SetProxyExtra(window_proxy.get(), 0, &PrivateValue(raw as *const _)); + // The window proxy owns the browsing context. + // When we finalize the window proxy, it drops the browsing context it owns. + SetProxyExtra(window_proxy.get(), 0, &PrivateValue(&*browsing_context as *const _ as *const _)); - (*raw).init_reflector(window_proxy.get()); + // Notify the JS engine about the new window proxy binding. + SetWindowProxy(cx, window_jsobject, window_proxy.handle()); - Root::from_ref(&*raw) + // Set the reflector. + debug!("Initializing reflector of {:p} to {:p}.", browsing_context, window_proxy.get()); + browsing_context.reflector.set_jsobject(window_proxy.get()); + Root::from_ref(&*Box::into_raw(browsing_context)) } } @@ -86,6 +100,50 @@ impl BrowsingContext { self.frame_element.r() } + #[allow(unsafe_code)] + /// Change the Window that this browsing context's WindowProxy resolves to. + // TODO: support setting the window proxy to a dummy value, + // to handle the case when the active document is in another script thread. + pub fn set_window_proxy(&self, window: &Window) { + unsafe { + debug!("Setting window proxy of {:p}.", self); + let WindowProxyHandler(handler) = window.windowproxy_handler(); + assert!(!handler.is_null()); + + let cx = window.get_cx(); + let window_jsobject = window.reflector().get_jsobject(); + let old_window_proxy = self.reflector.get_jsobject(); + assert!(!window_jsobject.get().is_null()); + assert!(((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL) != 0); + let _ac = JSAutoCompartment::new(cx, window_jsobject.get()); + + // The old window proxy no longer owns this browsing context. + SetProxyExtra(old_window_proxy.get(), 0, &PrivateValue(ptr::null_mut())); + + // Brain transpant the window proxy. + // We need to do this, because the Window and WindowProxy + // objects need to be in the same compartment. + // JS_TransplantObject does this by copying the contents + // of the old window proxy to the new window proxy, then + // making the old window proxy a cross-compartment wrapper + // pointing to the new window proxy. + rooted!(in(cx) let new_window_proxy = NewWindowProxy(cx, window_jsobject, handler)); + debug!("Transplanting window proxy from {:p} to {:p}.", old_window_proxy.get(), new_window_proxy.get()); + rooted!(in(cx) let new_window_proxy = JS_TransplantObject(cx, old_window_proxy, new_window_proxy.handle())); + debug!("Transplanted window proxy is {:p}.", new_window_proxy.get()); + + // Transfer ownership of this browsing context from the old window proxy to the new one. + SetProxyExtra(new_window_proxy.get(), 0, &PrivateValue(self as *const _ as *const _)); + + // Notify the JS engine about the new window proxy binding. + SetWindowProxy(cx, window_jsobject, new_window_proxy.handle()); + + // Update the reflector. + debug!("Setting reflector of {:p} to {:p}.", self, new_window_proxy.get()); + self.reflector.rootable().set(new_window_proxy.get()); + } + } + pub fn window_proxy(&self) -> *mut JSObject { let window_proxy = self.reflector.get_jsobject(); assert!(!window_proxy.get().is_null()); @@ -277,16 +335,20 @@ static PROXY_HANDLER: ProxyTraps = ProxyTraps { #[allow(unsafe_code)] unsafe extern fn finalize(_fop: *mut JSFreeOp, obj: *mut JSObject) { let this = GetProxyExtra(obj, 0).to_private() as *mut BrowsingContext; - assert!(!this.is_null()); + if this.is_null() { + // GC during obj creation or after transplanting. + return; + } + let jsobject = (*this).reflector.get_jsobject().get(); + debug!("BrowsingContext finalize: {:p}, with reflector {:p} from {:p}.", this, jsobject, obj); let _ = Box::from_raw(this); - debug!("BrowsingContext finalize: {:p}", this); } #[allow(unsafe_code)] unsafe extern fn trace(trc: *mut JSTracer, obj: *mut JSObject) { let this = GetProxyExtra(obj, 0).to_private() as *const BrowsingContext; if this.is_null() { - // GC during obj creation + // GC during obj creation or after transplanting. return; } (*this).trace(trc); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index da4d715fe21a..be44ae0a91c7 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -184,13 +184,12 @@ impl PendingRestyle { pub struct Document { node: Node, window: JS, - /// https://html.spec.whatwg.org/multipage/#concept-document-bc - browsing_context: Option>, implementation: MutNullableJS, location: MutNullableJS, content_type: DOMString, last_modified: Option, encoding: Cell, + has_browsing_context: bool, is_html_document: bool, activity: Cell, url: DOMRefCell, @@ -369,8 +368,12 @@ impl Document { /// https://html.spec.whatwg.org/multipage/#concept-document-bc #[inline] - pub fn browsing_context(&self) -> Option<&BrowsingContext> { - self.browsing_context.as_ref().map(|browsing_context| &**browsing_context) + pub fn browsing_context(&self) -> Option> { + if self.has_browsing_context { + Some(self.window.browsing_context()) + } else { + None + } } #[inline] @@ -398,7 +401,7 @@ impl Document { 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()); + assert!(self.has_browsing_context); // Set the document's activity level, reflow if necessary, and suspend or resume timers. if activity != self.activity.get() { self.activity.set(activity); @@ -1568,7 +1571,7 @@ impl Document { self.process_deferred_scripts(); }, LoadType::PageSource(_) => { - if self.browsing_context.is_some() { + if self.has_browsing_context { // Disarm the reflow timer and trigger the initial reflow. self.reflow_timeout.set(None); self.upcast::().dirty(NodeDamage::OtherNodeDamage); @@ -1830,7 +1833,7 @@ impl Document { /// https://html.spec.whatwg.org/multipage/#cookie-averse-document-object pub fn is_cookie_averse(&self) -> bool { - self.browsing_context.is_none() || !url_has_network_scheme(&self.url()) + !self.has_browsing_context || !url_has_network_scheme(&self.url()) } pub fn nodes_from_point(&self, client_point: &Point2D) -> Vec { @@ -1901,9 +1904,15 @@ fn url_has_network_scheme(url: &ServoUrl) -> bool { } } +#[derive(Copy, Clone, HeapSizeOf, JSTraceable, PartialEq, Eq)] +pub enum HasBrowsingContext { + No, + Yes, +} + impl Document { pub fn new_inherited(window: &Window, - browsing_context: Option<&BrowsingContext>, + has_browsing_context: HasBrowsingContext, url: Option, origin: Origin, is_html_document: IsHTMLDocument, @@ -1926,7 +1935,7 @@ impl Document { Document { node: Node::new_document_node(), window: JS::from_ref(window), - browsing_context: browsing_context.map(JS::from_ref), + has_browsing_context: has_browsing_context == HasBrowsingContext::Yes, implementation: Default::default(), location: Default::default(), content_type: match content_type { @@ -1970,7 +1979,7 @@ impl Document { deferred_scripts: Default::default(), asap_in_order_scripts_list: Default::default(), asap_scripts_set: Default::default(), - scripting_enabled: browsing_context.is_some(), + scripting_enabled: has_browsing_context == HasBrowsingContext::Yes, animation_frame_ident: Cell::new(0), animation_frame_list: DOMRefCell::new(vec![]), running_animation_callbacks: Cell::new(false), @@ -2007,7 +2016,7 @@ impl Document { let doc = window.Document(); let docloader = DocumentLoader::new(&*doc.loader()); Ok(Document::new(window, - None, + HasBrowsingContext::No, None, doc.origin().alias(), IsHTMLDocument::NonHTMLDocument, @@ -2021,7 +2030,7 @@ impl Document { } pub fn new(window: &Window, - browsing_context: Option<&BrowsingContext>, + has_browsing_context: HasBrowsingContext, url: Option, origin: Origin, doctype: IsHTMLDocument, @@ -2034,7 +2043,7 @@ impl Document { referrer_policy: Option) -> Root { let document = reflect_dom_object(box Document::new_inherited(window, - browsing_context, + has_browsing_context, url, origin, doctype, @@ -2107,7 +2116,7 @@ impl Document { IsHTMLDocument::NonHTMLDocument }; let new_doc = Document::new(self.window(), - None, + HasBrowsingContext::No, None, // https://github.com/whatwg/html/issues/2109 Origin::opaque_identifier(), @@ -3011,10 +3020,10 @@ impl DocumentMethods for Document { // https://html.spec.whatwg.org/multipage/#dom-document-defaultview fn GetDefaultView(&self) -> Option> { - if self.browsing_context.is_none() { - None - } else { + if self.has_browsing_context { Some(Root::from_ref(&*self.window)) + } else { + None } } diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs index 45983c3292d4..4ec27cc5b367 100644 --- a/components/script/dom/domimplementation.rs +++ b/components/script/dom/domimplementation.rs @@ -13,7 +13,7 @@ use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::bindings::xmlname::{namespace_from_domstring, validate_qualified_name}; -use dom::document::{Document, IsHTMLDocument}; +use dom::document::{Document, HasBrowsingContext, IsHTMLDocument}; use dom::document::DocumentSource; use dom::documenttype::DocumentType; use dom::htmlbodyelement::HTMLBodyElement; @@ -78,7 +78,7 @@ impl DOMImplementationMethods for DOMImplementation { // Step 1. let doc = XMLDocument::new(win, - None, + HasBrowsingContext::No, None, self.document.origin().alias(), IsHTMLDocument::NonHTMLDocument, @@ -125,7 +125,7 @@ impl DOMImplementationMethods for DOMImplementation { // Step 1-2. let doc = Document::new(win, - None, + HasBrowsingContext::No, None, self.document.origin().alias(), IsHTMLDocument::HTMLDocument, diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 54132ef94a92..32b5c5343a1c 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -15,7 +15,7 @@ use dom::bindings::error::Fallible; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::document::{Document, IsHTMLDocument}; +use dom::document::{Document, HasBrowsingContext, IsHTMLDocument}; use dom::document::DocumentSource; use dom::servoparser::ServoParser; use dom::window::Window; @@ -60,7 +60,7 @@ impl DOMParserMethods for DOMParser { match ty { Text_html => { let document = Document::new(&self.window, - None, + HasBrowsingContext::No, Some(url.clone()), doc.origin().alias(), IsHTMLDocument::HTMLDocument, @@ -78,7 +78,7 @@ impl DOMParserMethods for DOMParser { Text_xml | Application_xml | Application_xhtml_xml => { // FIXME: this should probably be FromParser when we actually parse the string (#3756). let document = Document::new(&self.window, - None, + HasBrowsingContext::No, Some(url.clone()), doc.origin().alias(), IsHTMLDocument::NonHTMLDocument, diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 6931fca18227..37b5764c1748 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -29,7 +29,7 @@ use dom::bindings::str::{DOMString, USVString}; use dom::bindings::xmlname::namespace_from_domstring; use dom::characterdata::{CharacterData, LayoutCharacterDataHelpers}; use dom::cssstylesheet::CSSStyleSheet; -use dom::document::{Document, DocumentSource, IsHTMLDocument}; +use dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument}; use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; use dom::element::{Element, ElementCreator}; @@ -1726,7 +1726,7 @@ impl Node { }; let window = document.window(); let loader = DocumentLoader::new(&*document.loader()); - let document = Document::new(window, None, + let document = Document::new(window, HasBrowsingContext::No, Some(document.url()), // https://github.com/whatwg/dom/issues/378 document.origin().alias(), diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index d64704b23686..0d8e02d273e9 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -14,7 +14,7 @@ use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::characterdata::CharacterData; -use dom::document::{Document, DocumentSource, IsHTMLDocument}; +use dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument}; use dom::element::Element; use dom::globalscope::GlobalScope; use dom::htmlformelement::HTMLFormElement; @@ -102,7 +102,7 @@ impl ServoParser { let loader = DocumentLoader::new_with_threads(context_document.loader().resource_threads().clone(), Some(url.clone())); let document = Document::new(window, - None, + HasBrowsingContext::No, Some(url.clone()), context_document.origin().alias(), IsHTMLDocument::HTMLDocument, diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 37c62543526b..86957d943d94 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -50,7 +50,7 @@ use fetch; use gfx_traits::ScrollRootId; use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::{HandleObject, HandleValue, JSAutoCompartment, JSContext}; -use js::jsapi::{JS_GC, JS_GetRuntime, SetWindowProxy}; +use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::{FrameType, PipelineId}; @@ -1375,10 +1375,6 @@ impl Window { pub fn init_browsing_context(&self, browsing_context: &BrowsingContext) { assert!(self.browsing_context.get().is_none()); self.browsing_context.set(Some(&browsing_context)); - let window = self.reflector().get_jsobject(); - let cx = self.get_cx(); - let _ac = JSAutoCompartment::new(cx, window.get()); - unsafe { SetWindowProxy(cx, window, browsing_context.reflector().get_jsobject()); } } #[allow(unsafe_code)] @@ -1489,7 +1485,11 @@ impl Window { } pub fn suspend(&self) { + // Suspend timer events. self.upcast::().suspend(); + + // TODO: set the window proxy to resolve to an object which throws security errors. #15233 + // 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, @@ -1498,7 +1498,15 @@ impl Window { } pub fn resume(&self) { + // Resume timer events. self.upcast::().resume(); + + // Set the window proxy to be this object. + self.browsing_context().set_window_proxy(&self); + + // 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 593749c44993..eaf90fbfe013 100644 --- a/components/script/dom/xmldocument.rs +++ b/components/script/dom/xmldocument.rs @@ -10,8 +10,7 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; -use dom::browsingcontext::BrowsingContext; -use dom::document::{Document, DocumentSource, IsHTMLDocument}; +use dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument}; use dom::location::Location; use dom::node::Node; use dom::window::Window; @@ -28,7 +27,7 @@ pub struct XMLDocument { impl XMLDocument { fn new_inherited(window: &Window, - browsing_context: Option<&BrowsingContext>, + has_browsing_context: HasBrowsingContext, url: Option, origin: Origin, is_html_document: IsHTMLDocument, @@ -39,7 +38,7 @@ impl XMLDocument { doc_loader: DocumentLoader) -> XMLDocument { XMLDocument { document: Document::new_inherited(window, - browsing_context, + has_browsing_context, url, origin, is_html_document, @@ -54,7 +53,7 @@ impl XMLDocument { } pub fn new(window: &Window, - browsing_context: Option<&BrowsingContext>, + has_browsing_context: HasBrowsingContext, url: Option, origin: Origin, doctype: IsHTMLDocument, @@ -66,7 +65,7 @@ impl XMLDocument { -> Root { let doc = reflect_dom_object( box XMLDocument::new_inherited(window, - browsing_context, + has_browsing_context, url, origin, doctype, diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index f42afe87f8b2..6fe89476994b 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -20,7 +20,7 @@ use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{DomObject, reflect_dom_object}; use dom::bindings::str::{ByteString, DOMString, USVString, is_token}; use dom::blob::{Blob, BlobImpl}; -use dom::document::{Document, IsHTMLDocument}; +use dom::document::{Document, HasBrowsingContext, IsHTMLDocument}; use dom::document::DocumentSource; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; @@ -1223,7 +1223,7 @@ impl XMLHttpRequest { DOMString::from(format!("{}", mime)) }); Document::new(win, - None, + HasBrowsingContext::No, parsed_url, doc.origin().alias(), is_html_document, diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 2bdb75a486f8..e1e07fd177e8 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; @@ -391,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, + /// The browsing contexts known by this thread + /// TODO: this map grows, but never shrinks. Issue #15258. + browsing_contexts: DOMRefCell>>, /// A list of data pertaining to loads that have not yet received a network response incomplete_loads: DOMRefCell>, /// A map to store service worker registrations for a given origin @@ -651,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()), @@ -1514,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); } @@ -1693,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)) @@ -1735,7 +1745,7 @@ impl ScriptThread { .map(ReferrerPolicy::from); let document = Document::new(&window, - Some(&browsing_context), + HasBrowsingContext::Yes, Some(final_url.clone()), incomplete.origin, is_html_document, @@ -2139,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() } diff --git a/tests/wpt/metadata/html/browsers/the-window-object/Window-document.html.ini b/tests/wpt/metadata/html/browsers/the-window-object/Window-document.html.ini deleted file mode 100644 index 382f076961f6..000000000000 --- a/tests/wpt/metadata/html/browsers/the-window-object/Window-document.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[Window-document.html] - type: testharness - [Document in a browsing context] - expected: FAIL -