mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Allow windows to share browsing contexts.
This commit is contained in:
parent
fc67878edf
commit
403499a9d5
11 changed files with 168 additions and 108 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue