mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
Allow browsing contexts to resolve to cross-origin windows.
This commit is contained in:
parent
4a0b730caf
commit
e8d765557f
11 changed files with 598 additions and 16 deletions
|
@ -3,13 +3,17 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
|
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
|
||||||
|
use dom::bindings::error::{Error, throw_dom_exception};
|
||||||
|
use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::js::{JS, Root, RootedReference};
|
use dom::bindings::js::{JS, Root, RootedReference};
|
||||||
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
|
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
|
||||||
use dom::bindings::reflector::{DomObject, Reflector};
|
use dom::bindings::reflector::{DomObject, Reflector};
|
||||||
use dom::bindings::trace::JSTraceable;
|
use dom::bindings::trace::JSTraceable;
|
||||||
use dom::bindings::utils::WindowProxyHandler;
|
use dom::bindings::utils::WindowProxyHandler;
|
||||||
use dom::bindings::utils::get_array_index_from_id;
|
use dom::bindings::utils::get_array_index_from_id;
|
||||||
|
use dom::dissimilaroriginwindow::DissimilarOriginWindow;
|
||||||
use dom::element::Element;
|
use dom::element::Element;
|
||||||
|
use dom::globalscope::GlobalScope;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use js::JSCLASS_IS_GLOBAL;
|
use js::JSCLASS_IS_GLOBAL;
|
||||||
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
|
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
|
||||||
|
@ -18,12 +22,13 @@ use js::jsapi::{Handle, HandleId, HandleObject, HandleValue};
|
||||||
use js::jsapi::{JSAutoCompartment, JSContext, JSErrNum, JSFreeOp, JSObject};
|
use js::jsapi::{JSAutoCompartment, JSContext, JSErrNum, JSFreeOp, JSObject};
|
||||||
use js::jsapi::{JSPROP_READONLY, JSTracer, JS_DefinePropertyById};
|
use js::jsapi::{JSPROP_READONLY, JSTracer, JS_DefinePropertyById};
|
||||||
use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo};
|
use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo};
|
||||||
use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById};
|
use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById, JS_HasOwnPropertyById};
|
||||||
use js::jsapi::{JS_TransplantObject, SetWindowProxy};
|
use js::jsapi::{JS_IsExceptionPending, JS_TransplantObject, SetWindowProxy};
|
||||||
use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue};
|
use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue};
|
||||||
use js::jsapi::{ObjectOpResult, PropertyDescriptor};
|
use js::jsapi::{ObjectOpResult, PropertyDescriptor};
|
||||||
use js::jsval::{UndefinedValue, PrivateValue};
|
use js::jsval::{UndefinedValue, PrivateValue};
|
||||||
use js::rust::get_object_class;
|
use js::rust::get_object_class;
|
||||||
|
use msg::constellation_msg::PipelineId;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
@ -39,6 +44,13 @@ pub struct BrowsingContext {
|
||||||
/// changes Window.
|
/// changes Window.
|
||||||
reflector: Reflector,
|
reflector: Reflector,
|
||||||
|
|
||||||
|
/// The pipeline id of the currently active document.
|
||||||
|
/// May be None, when the currently active document is in another script thread.
|
||||||
|
/// We do not try to keep the pipeline id for documents in other threads,
|
||||||
|
/// as this would require the constellation notifying many script threads about
|
||||||
|
/// the change, which could be expensive.
|
||||||
|
currently_active: Cell<Option<PipelineId>>,
|
||||||
|
|
||||||
/// Has this browsing context been discarded?
|
/// Has this browsing context been discarded?
|
||||||
discarded: Cell<bool>,
|
discarded: Cell<bool>,
|
||||||
|
|
||||||
|
@ -47,9 +59,10 @@ pub struct BrowsingContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BrowsingContext {
|
impl BrowsingContext {
|
||||||
pub fn new_inherited(frame_element: Option<&Element>) -> BrowsingContext {
|
pub fn new_inherited(currently_active: PipelineId, frame_element: Option<&Element>) -> BrowsingContext {
|
||||||
BrowsingContext {
|
BrowsingContext {
|
||||||
reflector: Reflector::new(),
|
reflector: Reflector::new(),
|
||||||
|
currently_active: Cell::new(Some(currently_active)),
|
||||||
discarded: Cell::new(false),
|
discarded: Cell::new(false),
|
||||||
frame_element: frame_element.map(JS::from_ref),
|
frame_element: frame_element.map(JS::from_ref),
|
||||||
}
|
}
|
||||||
|
@ -72,7 +85,8 @@ impl BrowsingContext {
|
||||||
assert!(!window_proxy.is_null());
|
assert!(!window_proxy.is_null());
|
||||||
|
|
||||||
// Create a new browsing context.
|
// Create a new browsing context.
|
||||||
let mut browsing_context = box BrowsingContext::new_inherited(frame_element);
|
let currently_active = window.global().pipeline_id();
|
||||||
|
let mut browsing_context = box BrowsingContext::new_inherited(currently_active, frame_element);
|
||||||
|
|
||||||
// The window proxy owns the browsing context.
|
// The window proxy owns the browsing context.
|
||||||
// When we finalize the window proxy, it drops the browsing context it owns.
|
// When we finalize the window proxy, it drops the browsing context it owns.
|
||||||
|
@ -104,10 +118,10 @@ impl BrowsingContext {
|
||||||
/// Change the Window that this browsing context's WindowProxy resolves to.
|
/// Change the Window that this browsing context's WindowProxy resolves to.
|
||||||
// TODO: support setting the window proxy to a dummy value,
|
// TODO: support setting the window proxy to a dummy value,
|
||||||
// to handle the case when the active document is in another script thread.
|
// to handle the case when the active document is in another script thread.
|
||||||
pub fn set_window_proxy(&self, window: &Window) {
|
fn set_window_proxy(&self, window: &GlobalScope, traps: &ProxyTraps) {
|
||||||
unsafe {
|
unsafe {
|
||||||
debug!("Setting window proxy of {:p}.", self);
|
debug!("Setting window proxy of {:p}.", self);
|
||||||
let WindowProxyHandler(handler) = window.windowproxy_handler();
|
let handler = CreateWrapperProxyHandler(traps);
|
||||||
assert!(!handler.is_null());
|
assert!(!handler.is_null());
|
||||||
|
|
||||||
let cx = window.get_cx();
|
let cx = window.get_cx();
|
||||||
|
@ -144,6 +158,22 @@ impl BrowsingContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_currently_active(&self, window: &Window) {
|
||||||
|
let globalscope = window.upcast();
|
||||||
|
self.set_window_proxy(&*globalscope, &PROXY_HANDLER);
|
||||||
|
self.currently_active.set(Some(globalscope.pipeline_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unset_currently_active(&self) {
|
||||||
|
let window = DissimilarOriginWindow::new(self);
|
||||||
|
self.set_window_proxy(&*window.upcast(), &XORIGIN_PROXY_HANDLER);
|
||||||
|
self.currently_active.set(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn currently_active(&self) -> Option<PipelineId> {
|
||||||
|
self.currently_active.get()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn window_proxy(&self) -> *mut JSObject {
|
pub fn window_proxy(&self) -> *mut JSObject {
|
||||||
let window_proxy = self.reflector.get_jsobject();
|
let window_proxy = self.reflector.get_jsobject();
|
||||||
assert!(!window_proxy.get().is_null());
|
assert!(!window_proxy.get().is_null());
|
||||||
|
@ -332,6 +362,145 @@ static PROXY_HANDLER: ProxyTraps = ProxyTraps {
|
||||||
isConstructor: None,
|
isConstructor: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn new_window_proxy_handler() -> WindowProxyHandler {
|
||||||
|
unsafe {
|
||||||
|
WindowProxyHandler(CreateWrapperProxyHandler(&PROXY_HANDLER))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The proxy traps for cross-origin windows.
|
||||||
|
// These traps often throw security errors, and only pass on calls to methods
|
||||||
|
// defined in the DissimilarOriginWindow IDL.
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe fn throw_security_error(cx: *mut JSContext) -> bool {
|
||||||
|
if !JS_IsExceptionPending(cx) {
|
||||||
|
let global = GlobalScope::from_context(cx);
|
||||||
|
throw_dom_exception(cx, &*global, Error::Security);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe extern "C" fn has_xorigin(cx: *mut JSContext,
|
||||||
|
proxy: HandleObject,
|
||||||
|
id: HandleId,
|
||||||
|
bp: *mut bool)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
|
||||||
|
let mut found = false;
|
||||||
|
JS_HasOwnPropertyById(cx, target.handle(), id, &mut found);
|
||||||
|
if found {
|
||||||
|
*bp = true;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
throw_security_error(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe extern "C" fn get_xorigin(cx: *mut JSContext,
|
||||||
|
proxy: HandleObject,
|
||||||
|
receiver: HandleValue,
|
||||||
|
id: HandleId,
|
||||||
|
vp: MutableHandleValue)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
let mut found = false;
|
||||||
|
has_xorigin(cx, proxy, id, &mut found);
|
||||||
|
found && get(cx, proxy, receiver, id, vp)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe extern "C" fn set_xorigin(cx: *mut JSContext,
|
||||||
|
_: HandleObject,
|
||||||
|
_: HandleId,
|
||||||
|
_: HandleValue,
|
||||||
|
_: HandleValue,
|
||||||
|
_: *mut ObjectOpResult)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
throw_security_error(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe extern "C" fn delete_xorigin(cx: *mut JSContext,
|
||||||
|
_: HandleObject,
|
||||||
|
_: HandleId,
|
||||||
|
_: *mut ObjectOpResult)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
throw_security_error(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe extern "C" fn getOwnPropertyDescriptor_xorigin(cx: *mut JSContext,
|
||||||
|
proxy: HandleObject,
|
||||||
|
id: HandleId,
|
||||||
|
desc: MutableHandle<PropertyDescriptor>)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
let mut found = false;
|
||||||
|
has_xorigin(cx, proxy, id, &mut found);
|
||||||
|
found && getOwnPropertyDescriptor(cx, proxy, id, desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe extern "C" fn defineProperty_xorigin(cx: *mut JSContext,
|
||||||
|
_: HandleObject,
|
||||||
|
_: HandleId,
|
||||||
|
_: Handle<PropertyDescriptor>,
|
||||||
|
_: *mut ObjectOpResult)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
throw_security_error(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe extern "C" fn preventExtensions_xorigin(cx: *mut JSContext,
|
||||||
|
_: HandleObject,
|
||||||
|
_: *mut ObjectOpResult)
|
||||||
|
-> bool
|
||||||
|
{
|
||||||
|
throw_security_error(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
static XORIGIN_PROXY_HANDLER: ProxyTraps = ProxyTraps {
|
||||||
|
enter: None,
|
||||||
|
getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor_xorigin),
|
||||||
|
defineProperty: Some(defineProperty_xorigin),
|
||||||
|
ownPropertyKeys: None,
|
||||||
|
delete_: Some(delete_xorigin),
|
||||||
|
enumerate: None,
|
||||||
|
getPrototypeIfOrdinary: None,
|
||||||
|
preventExtensions: Some(preventExtensions_xorigin),
|
||||||
|
isExtensible: None,
|
||||||
|
has: Some(has_xorigin),
|
||||||
|
get: Some(get_xorigin),
|
||||||
|
set: Some(set_xorigin),
|
||||||
|
call: None,
|
||||||
|
construct: None,
|
||||||
|
getPropertyDescriptor: Some(getOwnPropertyDescriptor_xorigin),
|
||||||
|
hasOwn: Some(has_xorigin),
|
||||||
|
getOwnEnumerablePropertyKeys: None,
|
||||||
|
nativeCall: None,
|
||||||
|
hasInstance: None,
|
||||||
|
objectClassIs: None,
|
||||||
|
className: None,
|
||||||
|
fun_toString: None,
|
||||||
|
boxedValue_unbox: None,
|
||||||
|
defaultValue: None,
|
||||||
|
trace: Some(trace),
|
||||||
|
finalize: Some(finalize),
|
||||||
|
objectMoved: None,
|
||||||
|
isCallable: None,
|
||||||
|
isConstructor: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// How WindowProxy objects are garbage collected.
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe extern fn finalize(_fop: *mut JSFreeOp, obj: *mut JSObject) {
|
unsafe extern fn finalize(_fop: *mut JSFreeOp, obj: *mut JSObject) {
|
||||||
let this = GetProxyExtra(obj, 0).to_private() as *mut BrowsingContext;
|
let this = GetProxyExtra(obj, 0).to_private() as *mut BrowsingContext;
|
||||||
|
@ -354,9 +523,3 @@ unsafe extern fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
|
||||||
(*this).trace(trc);
|
(*this).trace(trc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
pub fn new_window_proxy_handler() -> WindowProxyHandler {
|
|
||||||
unsafe {
|
|
||||||
WindowProxyHandler(CreateWrapperProxyHandler(&PROXY_HANDLER))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
80
components/script/dom/dissimilaroriginlocation.rs
Normal file
80
components/script/dom/dissimilaroriginlocation.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::DissimilarOriginLocationBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::DissimilarOriginLocationBinding::DissimilarOriginLocationMethods;
|
||||||
|
use dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||||
|
use dom::bindings::js::{JS, Root};
|
||||||
|
use dom::bindings::reflector::Reflector;
|
||||||
|
use dom::bindings::reflector::reflect_dom_object;
|
||||||
|
use dom::bindings::str::DOMString;
|
||||||
|
use dom::bindings::str::USVString;
|
||||||
|
use dom::dissimilaroriginwindow::DissimilarOriginWindow;
|
||||||
|
|
||||||
|
/// Represents a dissimilar-origin `Location` that exists in another script thread.
|
||||||
|
///
|
||||||
|
/// Since the `Location` is in a different script thread, we cannot access it
|
||||||
|
/// directly, but some of its accessors (for example setting `location.href`)
|
||||||
|
/// still need to function.
|
||||||
|
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct DissimilarOriginLocation {
|
||||||
|
/// The reflector. Once we have XOWs, this will have a cross-origin
|
||||||
|
/// wrapper placed around it.
|
||||||
|
reflector: Reflector,
|
||||||
|
|
||||||
|
/// The window associated with this location.
|
||||||
|
window: JS<DissimilarOriginWindow>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DissimilarOriginLocation {
|
||||||
|
#[allow(unrooted_must_root)]
|
||||||
|
fn new_inherited(window: &DissimilarOriginWindow) -> DissimilarOriginLocation {
|
||||||
|
DissimilarOriginLocation {
|
||||||
|
reflector: Reflector::new(),
|
||||||
|
window: JS::from_ref(window),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(window: &DissimilarOriginWindow) -> Root<DissimilarOriginLocation> {
|
||||||
|
reflect_dom_object(box DissimilarOriginLocation::new_inherited(window),
|
||||||
|
window,
|
||||||
|
DissimilarOriginLocationBinding::Wrap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DissimilarOriginLocationMethods for DissimilarOriginLocation {
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-location-href
|
||||||
|
fn GetHref(&self) -> Fallible<USVString> {
|
||||||
|
Err(Error::Security)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-location-href
|
||||||
|
fn SetHref(&self, _: USVString) -> ErrorResult {
|
||||||
|
// TODO: setting href on a cross-origin window should succeed?
|
||||||
|
Err(Error::Security)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-location-assign
|
||||||
|
fn Assign(&self, _: USVString) -> Fallible<()> {
|
||||||
|
// TODO: setting href on a cross-origin window should succeed?
|
||||||
|
Err(Error::Security)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-location-replace
|
||||||
|
fn Replace(&self, _: USVString) -> Fallible<()> {
|
||||||
|
// TODO: replacing href on a cross-origin window should succeed?
|
||||||
|
Err(Error::Security)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-location-reload
|
||||||
|
fn Reload(&self) -> Fallible<()> {
|
||||||
|
Err(Error::Security)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-location-href
|
||||||
|
fn Stringifier(&self) -> Fallible<DOMString> {
|
||||||
|
Err(Error::Security)
|
||||||
|
}
|
||||||
|
}
|
140
components/script/dom/dissimilaroriginwindow.rs
Normal file
140
components/script/dom/dissimilaroriginwindow.rs
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods;
|
||||||
|
use dom::bindings::js::{JS, MutNullableJS, Root};
|
||||||
|
use dom::bindings::reflector::DomObject;
|
||||||
|
use dom::bindings::str::DOMString;
|
||||||
|
use dom::browsingcontext::BrowsingContext;
|
||||||
|
use dom::dissimilaroriginlocation::DissimilarOriginLocation;
|
||||||
|
use dom::globalscope::GlobalScope;
|
||||||
|
use ipc_channel::ipc;
|
||||||
|
use js::jsapi::{JSContext, HandleValue};
|
||||||
|
use js::jsval::{JSVal, UndefinedValue};
|
||||||
|
use msg::constellation_msg::PipelineId;
|
||||||
|
|
||||||
|
/// Represents a dissimilar-origin `Window` that exists in another script thread.
|
||||||
|
///
|
||||||
|
/// Since the `Window` is in a different script thread, we cannot access it
|
||||||
|
/// directly, but some of its accessors (for example `window.parent`)
|
||||||
|
/// still need to function.
|
||||||
|
///
|
||||||
|
/// In `browsingcontext.rs`, we create a custom window proxy for these windows,
|
||||||
|
/// that throws security exceptions for most accessors. This is not a replacement
|
||||||
|
/// for XOWs, but provides belt-and-braces security.
|
||||||
|
#[dom_struct]
|
||||||
|
pub struct DissimilarOriginWindow {
|
||||||
|
/// The global for this window.
|
||||||
|
globalscope: GlobalScope,
|
||||||
|
|
||||||
|
/// The browsing context this window is part of.
|
||||||
|
browsing_context: JS<BrowsingContext>,
|
||||||
|
|
||||||
|
/// The location of this window, initialized lazily.
|
||||||
|
location: MutNullableJS<DissimilarOriginLocation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DissimilarOriginWindow {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn new(browsing_context: &BrowsingContext) -> Root<DissimilarOriginWindow> {
|
||||||
|
let globalscope = browsing_context.global();
|
||||||
|
let cx = globalscope.get_cx();
|
||||||
|
// Any timer events fired on this window are ignored.
|
||||||
|
let (timer_event_chan, _) = ipc::channel().unwrap();
|
||||||
|
let win = box DissimilarOriginWindow {
|
||||||
|
globalscope: GlobalScope::new_inherited(PipelineId::new(),
|
||||||
|
globalscope.devtools_chan().cloned(),
|
||||||
|
globalscope.mem_profiler_chan().clone(),
|
||||||
|
globalscope.time_profiler_chan().clone(),
|
||||||
|
globalscope.constellation_chan().clone(),
|
||||||
|
globalscope.scheduler_chan().clone(),
|
||||||
|
globalscope.resource_threads().clone(),
|
||||||
|
timer_event_chan),
|
||||||
|
browsing_context: JS::from_ref(browsing_context),
|
||||||
|
location: MutNullableJS::new(None),
|
||||||
|
};
|
||||||
|
unsafe { DissimilarOriginWindowBinding::Wrap(cx, win) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-window
|
||||||
|
fn Window(&self) -> Root<BrowsingContext> {
|
||||||
|
Root::from_ref(&*self.browsing_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-self
|
||||||
|
fn Self_(&self) -> Root<BrowsingContext> {
|
||||||
|
Root::from_ref(&*self.browsing_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-frames
|
||||||
|
fn Frames(&self) -> Root<BrowsingContext> {
|
||||||
|
Root::from_ref(&*self.browsing_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-parent
|
||||||
|
fn GetParent(&self) -> Option<Root<BrowsingContext>> {
|
||||||
|
// TODO: implement window.parent correctly for x-origin windows.
|
||||||
|
Some(Root::from_ref(&*self.browsing_context))
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-top
|
||||||
|
fn GetTop(&self) -> Option<Root<BrowsingContext>> {
|
||||||
|
// TODO: implement window.top correctly for x-origin windows.
|
||||||
|
Some(Root::from_ref(&*self.browsing_context))
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-length
|
||||||
|
fn Length(&self) -> u32 {
|
||||||
|
// TODO: Implement x-origin length
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-window-close
|
||||||
|
fn Close(&self) {
|
||||||
|
// TODO: Implement x-origin close
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-window-closed
|
||||||
|
fn Closed(&self) -> bool {
|
||||||
|
// TODO: Implement x-origin close
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
|
||||||
|
unsafe fn PostMessage(&self, _: *mut JSContext, _: HandleValue, _: DOMString) {
|
||||||
|
// TODO: Implement x-origin postMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-opener
|
||||||
|
unsafe fn Opener(&self, _: *mut JSContext) -> JSVal {
|
||||||
|
// TODO: Implement x-origin opener
|
||||||
|
UndefinedValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-opener
|
||||||
|
unsafe fn SetOpener(&self, _: *mut JSContext, _: HandleValue) {
|
||||||
|
// TODO: Implement x-origin opener
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-window-blur
|
||||||
|
fn Blur(&self) {
|
||||||
|
// TODO: Implement x-origin blur
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-focus
|
||||||
|
fn Focus(&self) {
|
||||||
|
// TODO: Implement x-origin focus
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#dom-location
|
||||||
|
fn Location(&self) -> Root<DissimilarOriginLocation> {
|
||||||
|
self.location.or_init(|| DissimilarOriginLocation::new(self))
|
||||||
|
}
|
||||||
|
}
|
|
@ -258,6 +258,8 @@ pub mod csssupportsrule;
|
||||||
pub mod cssviewportrule;
|
pub mod cssviewportrule;
|
||||||
pub mod customevent;
|
pub mod customevent;
|
||||||
pub mod dedicatedworkerglobalscope;
|
pub mod dedicatedworkerglobalscope;
|
||||||
|
pub mod dissimilaroriginlocation;
|
||||||
|
pub mod dissimilaroriginwindow;
|
||||||
pub mod document;
|
pub mod document;
|
||||||
pub mod documentfragment;
|
pub mod documentfragment;
|
||||||
pub mod documenttype;
|
pub mod documenttype;
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
|
||||||
|
// This is a Servo-specific interface, used to represent locations
|
||||||
|
// that are not similar-origin, so live in another script thread.
|
||||||
|
// It is based on the interface for Window, but only contains the
|
||||||
|
// accessors that do not throw security exceptions when called
|
||||||
|
// cross-origin.
|
||||||
|
//
|
||||||
|
// Note that similar-origin locations are kept in the same script
|
||||||
|
// thread, so this mechanism cannot be relied upon as the only
|
||||||
|
// way to enforce security policy.
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#location
|
||||||
|
[Unforgeable, NoInterfaceObject] interface DissimilarOriginLocation {
|
||||||
|
[Throws] attribute USVString href;
|
||||||
|
[Throws] void assign(USVString url);
|
||||||
|
[Throws] void replace(USVString url);
|
||||||
|
[Throws] void reload();
|
||||||
|
[Throws] stringifier;
|
||||||
|
|
||||||
|
// TODO: finish this interface
|
||||||
|
};
|
32
components/script/dom/webidls/DissimilarOriginWindow.webidl
Normal file
32
components/script/dom/webidls/DissimilarOriginWindow.webidl
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
// This is a Servo-specific interface, used to represent windows
|
||||||
|
// that are not similar-origin, so live in another script thread.
|
||||||
|
// It is based on the interface for Window, but only contains the
|
||||||
|
// accessors that do not throw security exceptions when called
|
||||||
|
// cross-origin.
|
||||||
|
//
|
||||||
|
// Note that similar-origin windows are kept in the same script
|
||||||
|
// thread, so this mechanism cannot be relied upon as the only
|
||||||
|
// way to enforce security policy.
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#window
|
||||||
|
[Global, NoInterfaceObject]
|
||||||
|
interface DissimilarOriginWindow : GlobalScope {
|
||||||
|
[Unforgeable] readonly attribute WindowProxy window;
|
||||||
|
[BinaryName="Self_", Replaceable] readonly attribute WindowProxy self;
|
||||||
|
[Unforgeable] readonly attribute WindowProxy? parent;
|
||||||
|
[Unforgeable] readonly attribute WindowProxy? top;
|
||||||
|
[Replaceable] readonly attribute WindowProxy frames;
|
||||||
|
[Replaceable] readonly attribute unsigned long length;
|
||||||
|
[Unforgeable] readonly attribute DissimilarOriginLocation location;
|
||||||
|
|
||||||
|
void close();
|
||||||
|
readonly attribute boolean closed;
|
||||||
|
void postMessage(any message, DOMString targetOrigin);
|
||||||
|
attribute any opener;
|
||||||
|
void blur();
|
||||||
|
void focus();
|
||||||
|
};
|
|
@ -1514,7 +1514,10 @@ impl Window {
|
||||||
// Suspend timer events.
|
// Suspend timer events.
|
||||||
self.upcast::<GlobalScope>().suspend();
|
self.upcast::<GlobalScope>().suspend();
|
||||||
|
|
||||||
// TODO: set the window proxy to resolve to an object which throws security errors. #15233
|
// Set the window proxy to be a cross-origin window.
|
||||||
|
if self.browsing_context().currently_active() == Some(self.global().pipeline_id()) {
|
||||||
|
self.browsing_context().unset_currently_active();
|
||||||
|
}
|
||||||
|
|
||||||
// A hint to the JS runtime that now would be a good time to
|
// A hint to the JS runtime that now would be a good time to
|
||||||
// GC any unreachable objects generated by user script,
|
// GC any unreachable objects generated by user script,
|
||||||
|
@ -1528,7 +1531,7 @@ impl Window {
|
||||||
self.upcast::<GlobalScope>().resume();
|
self.upcast::<GlobalScope>().resume();
|
||||||
|
|
||||||
// Set the window proxy to be this object.
|
// Set the window proxy to be this object.
|
||||||
self.browsing_context().set_window_proxy(&self);
|
self.browsing_context().set_currently_active(self);
|
||||||
|
|
||||||
// Push the document title to the compositor since we are
|
// Push the document title to the compositor since we are
|
||||||
// activating this document due to a navigation.
|
// activating this document due to a navigation.
|
||||||
|
|
|
@ -1678,7 +1678,7 @@ impl ScriptThread {
|
||||||
},
|
},
|
||||||
hash_map::Entry::Occupied(entry) => {
|
hash_map::Entry::Occupied(entry) => {
|
||||||
let browsing_context = entry.get();
|
let browsing_context = entry.get();
|
||||||
browsing_context.set_window_proxy(&window);
|
browsing_context.set_currently_active(&*window);
|
||||||
window.init_browsing_context(browsing_context);
|
window.init_browsing_context(browsing_context);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1782,7 +1782,9 @@ impl ScriptThread {
|
||||||
ServoParser::parse_html_document(&document, parse_input, final_url);
|
ServoParser::parse_html_document(&document, parse_input, final_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
if incomplete.activity != DocumentActivity::FullyActive {
|
if incomplete.activity == DocumentActivity::FullyActive {
|
||||||
|
window.resume();
|
||||||
|
} else {
|
||||||
window.suspend();
|
window.suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12721,6 +12721,14 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"mozilla/cross-origin-objects/cross-origin-objects.html": [
|
||||||
|
[
|
||||||
|
"/_mozilla/mozilla/cross-origin-objects/cross-origin-objects.html",
|
||||||
|
{
|
||||||
|
"timeout": "long"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
"mozilla/deterministic-raf.html": [
|
"mozilla/deterministic-raf.html": [
|
||||||
[
|
[
|
||||||
"/_mozilla/mozilla/deterministic-raf.html",
|
"/_mozilla/mozilla/deterministic-raf.html",
|
||||||
|
@ -25350,6 +25358,10 @@
|
||||||
"f1029d519aa7017a1a3d18a891a0774b9a39f847",
|
"f1029d519aa7017a1a3d18a891a0774b9a39f847",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
|
"mozilla/cross-origin-objects/cross-origin-objects.html": [
|
||||||
|
"5d5a3ba4099dfabddbed1ea98ad8fe1f5c00a3d3",
|
||||||
|
"testharness"
|
||||||
|
],
|
||||||
"mozilla/details_ui_closed.html": [
|
"mozilla/details_ui_closed.html": [
|
||||||
"2acbe3afbec267bad4dd986803e636740a707507",
|
"2acbe3afbec267bad4dd986803e636740a707507",
|
||||||
"reftest"
|
"reftest"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[cross-origin-objects.html]
|
||||||
|
type: testharness
|
||||||
|
|
||||||
|
[Parentage of cross-origin windows]
|
||||||
|
expected: FAIL
|
|
@ -0,0 +1,118 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<meta name="timeout" content="long">
|
||||||
|
<title>Cross-origin behavior of Window and Location</title>
|
||||||
|
<link rel="author" title="Bobby Holley (:bholley)" href="bobbyholley@gmail.com">
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-window">
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/#security-location">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<div id=log></div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a stripped down version of
|
||||||
|
* /html/browsers/origin/cross-origin-objects/cross-origin-objects.html,
|
||||||
|
* which runs the tests in a dissimilar-origin iframe.
|
||||||
|
* This is a temporary work-around until the web-platform-tests supports
|
||||||
|
* more than one domain.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var UrlB = "/common/blank.html";
|
||||||
|
var UrlC = "http://127.0.0.1:8000/common/blank.html";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parentage
|
||||||
|
*/
|
||||||
|
|
||||||
|
async_test(function(t) {
|
||||||
|
var IframeB = document.createElement("iframe");
|
||||||
|
var IframeC = document.createElement("iframe");
|
||||||
|
var B = null;
|
||||||
|
var C = null;
|
||||||
|
IframeC.onload = t.step_func(function() {
|
||||||
|
if (!B || !C) {
|
||||||
|
B = IframeB.contentWindow;
|
||||||
|
C = IframeC.contentWindow;
|
||||||
|
IframeB.src = UrlB;
|
||||||
|
IframeC.src = UrlC;
|
||||||
|
} else {
|
||||||
|
assert_equals(B.parent, window, "window.parent works same-origin");
|
||||||
|
assert_equals(C.parent, window, "window.parent works cross-origin");
|
||||||
|
assert_equals(B.top, window, "window.top works same-origin");
|
||||||
|
assert_equals(C.top, window, "window.top works cross-origin");
|
||||||
|
t.done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.body.appendChild(IframeB);
|
||||||
|
document.body.appendChild(IframeC);
|
||||||
|
},"Parentage of cross-origin windows");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whitelist behavior.
|
||||||
|
*
|
||||||
|
* Also tests for [[GetOwnProperty]] and [[HasOwnProperty]] behavior.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var whitelistedWindowIndices = ['0', '1'];
|
||||||
|
var whitelistedWindowPropNames = ['location', 'postMessage', 'window', 'frames', 'self', 'top', 'parent',
|
||||||
|
'opener', 'closed', 'close', 'blur', 'focus', 'length'];
|
||||||
|
whitelistedWindowPropNames = whitelistedWindowPropNames.concat(whitelistedWindowIndices);
|
||||||
|
whitelistedWindowPropNames.sort();
|
||||||
|
var whitelistedLocationPropNames = ['href', 'replace'];
|
||||||
|
whitelistedLocationPropNames.sort();
|
||||||
|
var whitelistedSymbols = [Symbol.toStringTag, Symbol.hasInstance,
|
||||||
|
Symbol.isConcatSpreadable];
|
||||||
|
var whitelistedWindowProps = whitelistedWindowPropNames.concat(whitelistedSymbols);
|
||||||
|
|
||||||
|
async_test(function(t) {
|
||||||
|
var IframeB = document.createElement("iframe");
|
||||||
|
var IframeC = document.createElement("iframe");
|
||||||
|
var B = null;
|
||||||
|
var C = null;
|
||||||
|
IframeC.onload = t.step_func(function() {
|
||||||
|
if (!B || !C) {
|
||||||
|
B = IframeB.contentWindow;
|
||||||
|
C = IframeC.contentWindow;
|
||||||
|
IframeB.src = UrlB;
|
||||||
|
IframeC.src = UrlC;
|
||||||
|
} else {
|
||||||
|
for (var prop in window) {
|
||||||
|
if (whitelistedWindowProps.indexOf(prop) != -1) {
|
||||||
|
C[prop]; // Shouldn't throw.
|
||||||
|
Object.getOwnPropertyDescriptor(C, prop); // Shouldn't throw.
|
||||||
|
assert_true(Object.prototype.hasOwnProperty.call(C, prop), "hasOwnProperty for " + String(prop));
|
||||||
|
} else {
|
||||||
|
assert_throws("SecurityError", function() { C[prop]; }, "Should throw when accessing " + String(prop) + " on Window");
|
||||||
|
assert_throws("SecurityError", function() { Object.getOwnPropertyDescriptor(C, prop); },
|
||||||
|
"Should throw when accessing property descriptor for " + prop + " on Window");
|
||||||
|
assert_throws("SecurityError", function() { Object.prototype.hasOwnProperty.call(C, prop); },
|
||||||
|
"Should throw when invoking hasOwnProperty for " + prop + " on Window");
|
||||||
|
}
|
||||||
|
if (prop != 'location')
|
||||||
|
assert_throws("SecurityError", function() { C[prop] = undefined; }, "Should throw when writing to " + prop + " on Window");
|
||||||
|
}
|
||||||
|
for (var prop in location) {
|
||||||
|
if (prop == 'replace') {
|
||||||
|
C.location[prop]; // Shouldn't throw.
|
||||||
|
Object.getOwnPropertyDescriptor(C.location, prop); // Shouldn't throw.
|
||||||
|
assert_true(Object.prototype.hasOwnProperty.call(C.location, prop), "hasOwnProperty for " + prop);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert_throws("SecurityError", function() { C[prop]; }, "Should throw when accessing " + prop + " on Location");
|
||||||
|
assert_throws("SecurityError", function() { Object.getOwnPropertyDescriptor(C, prop); },
|
||||||
|
"Should throw when accessing property descriptor for " + prop + " on Location");
|
||||||
|
assert_throws("SecurityError", function() { Object.prototype.hasOwnProperty.call(C, prop); },
|
||||||
|
"Should throw when invoking hasOwnProperty for " + prop + " on Location");
|
||||||
|
}
|
||||||
|
if (prop != 'href')
|
||||||
|
assert_throws("SecurityError", function() { C[prop] = undefined; }, "Should throw when writing to " + prop + " on Location");
|
||||||
|
}
|
||||||
|
t.done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
document.body.appendChild(IframeB);
|
||||||
|
document.body.appendChild(IframeC);
|
||||||
|
}, "Only whitelisted properties are accessible cross-origin");
|
||||||
|
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue