servo: Merge #15120 - Allow windows to share browsing contexts (from asajeffrey:script-windows-share-browsing-contexts); r=jdm

<!-- Please describe your changes on the following line: -->

This PR allows different `Window` objects in the same browsing context to share a `BrowsingContext` object.

SpiderMonkey requires a `WindowProxy` object to be in the same compartment as its `Window`, so when a `WindowProxy` changes `Window`, we have to brain-transplant it. In turn this requires the reflector of a `BrowsingContext` to be mutable.

---
<!-- 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 #13608 and #14843
- [X] These changes do not require tests because an existing test catches this (`/html/browsers/the-window-object/Window-document.html` is now `PASS`)

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: 67c182638253211553161495cd2e4570002fd5bc
This commit is contained in:
Alan Jeffrey 2017-01-28 20:35:39 -08:00
parent aa1b5f335e
commit 612d31497f
17 changed files with 177 additions and 116 deletions

View File

@ -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);

View File

@ -184,13 +184,12 @@ impl PendingRestyle {
pub struct Document {
node: Node,
window: JS<Window>,
/// https://html.spec.whatwg.org/multipage/#concept-document-bc
browsing_context: Option<JS<BrowsingContext>>,
implementation: MutNullableJS<DOMImplementation>,
location: MutNullableJS<Location>,
content_type: DOMString,
last_modified: Option<String>,
encoding: Cell<EncodingRef>,
has_browsing_context: bool,
is_html_document: bool,
activity: Cell<DocumentActivity>,
url: DOMRefCell<ServoUrl>,
@ -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<Root<BrowsingContext>> {
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::<Node>().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<f32>) -> Vec<UntrustedNodeAddress> {
@ -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<ServoUrl>,
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<ServoUrl>,
origin: Origin,
doctype: IsHTMLDocument,
@ -2034,7 +2043,7 @@ impl Document {
referrer_policy: Option<ReferrerPolicy>)
-> Root<Document> {
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<Root<Window>> {
if self.browsing_context.is_none() {
None
} else {
if self.has_browsing_context {
Some(Root::from_ref(&*self.window))
} else {
None
}
}

View File

@ -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,

View File

@ -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,

View File

@ -17,7 +17,6 @@ use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
use js::jsapi::{HandleValue, Heap, JSContext};
use js::jsval::JSVal;
use servo_atoms::Atom;
use std::default::Default;
#[dom_struct]
pub struct ExtendableMessageEvent {
@ -32,13 +31,12 @@ impl ExtendableMessageEvent {
bubbles: bool, cancelable: bool,
data: HandleValue, origin: DOMString, lastEventId: DOMString)
-> Root<ExtendableMessageEvent> {
let mut ev = box ExtendableMessageEvent {
let ev = box ExtendableMessageEvent {
event: ExtendableEvent::new_inherited(),
data: Heap::default(),
data: Heap::new(data.get()),
origin: origin,
lastEventId: lastEventId,
};
ev.data.set(data.get());
let ev = reflect_dom_object(ev, global, ExtendableMessageEventBinding::Wrap);
{
let event = ev.upcast::<Event>();

View File

@ -27,7 +27,7 @@ pub struct ImageData {
impl ImageData {
#[allow(unsafe_code)]
pub fn new(global: &GlobalScope, width: u32, height: u32, data: Option<Vec<u8>>) -> Root<ImageData> {
let mut imagedata = box ImageData {
let imagedata = box ImageData {
reflector_: Reflector::new(),
width: width,
height: height,

View File

@ -16,7 +16,6 @@ use dom::globalscope::GlobalScope;
use js::jsapi::{HandleValue, Heap, JSContext};
use js::jsval::JSVal;
use servo_atoms::Atom;
use std::default::Default;
#[dom_struct]
pub struct MessageEvent {
@ -38,13 +37,12 @@ impl MessageEvent {
data: HandleValue,
origin: DOMString,
lastEventId: DOMString) -> Root<MessageEvent> {
let mut ev = box MessageEvent {
let ev = box MessageEvent {
event: Event::new_inherited(),
data: Heap::default(),
data: Heap::new(data.get()),
origin: origin,
lastEventId: lastEventId,
};
ev.data.set(data.get());
reflect_dom_object(ev, global, MessageEventBinding::Wrap)
}

View File

@ -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(),

View File

@ -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,

View File

@ -31,7 +31,7 @@ impl VREyeParameters {
#[allow(unrooted_must_root)]
fn new_inherited(parameters: WebVREyeParameters, global: &GlobalScope) -> VREyeParameters {
let fov = VRFieldOfView::new(&global, parameters.field_of_view.clone());
let mut result = VREyeParameters {
let result = VREyeParameters {
reflector_: Reflector::new(),
parameters: DOMRefCell::new(parameters),
offset: Heap::default(),

View File

@ -39,7 +39,7 @@ impl VRFrameData {
0.0, 0.0, 0.0, 1.0f32];
let pose = VRPose::new(&global, &Default::default());
let mut framedata = VRFrameData {
let framedata = VRFrameData {
reflector_: Reflector::new(),
left_proj: Heap::default(),
left_view: Heap::default(),

View File

@ -29,7 +29,7 @@ pub struct VRPose {
unsafe fn update_or_create_typed_array(cx: *mut JSContext,
src: Option<&[f32]>,
dst: &DOMRefCell<Heap<*mut JSObject>>) {
let mut dst = dst.borrow_mut();
let dst = dst.borrow();
match src {
Some(ref data) => {
if dst.get().is_null() {

View File

@ -28,7 +28,7 @@ impl VRStageParameters {
#[allow(unsafe_code)]
#[allow(unrooted_must_root)]
fn new_inherited(parameters: WebVRStageParameters, global: &GlobalScope) -> VRStageParameters {
let mut stage = VRStageParameters {
let stage = VRStageParameters {
reflector_: Reflector::new(),
parameters: DOMRefCell::new(parameters),
transform: Heap::default()

View File

@ -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::<GlobalScope>().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::<GlobalScope>().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 {

View File

@ -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<ServoUrl>,
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<ServoUrl>,
origin: Origin,
doctype: IsHTMLDocument,
@ -66,7 +65,7 @@ impl XMLDocument {
-> Root<XMLDocument> {
let doc = reflect_dom_object(
box XMLDocument::new_inherited(window,
browsing_context,
has_browsing_context,
url,
origin,
doctype,

View File

@ -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,

View File

@ -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<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
@ -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()
}