Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow windows to share browsing contexts. #15120

Merged
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Prev

Allow windows to share browsing contexts.

  • Loading branch information
asajeffrey committed Jan 29, 2017
commit 403499a9d5aa3749571ef960f9ab909855570a6f
@@ -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,18 +19,24 @@ 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:
// here, in script, but also in the constellation. The constellation
// 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);
@@ -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
}
}

@@ -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,
@@ -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,
@@ -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(),
@@ -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,
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.