Skip to content

Commit

Permalink
Auto merge of #15358 - asajeffrey:script-browsingcontext-xorigin-wind…
Browse files Browse the repository at this point in the history
…ow, r=jdm

Allow browsing contexts to resolve to cross-origin windows

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

This PR implements cross-thread `WindowProxy` objects.

At the moment, if a `Window` performs a non-similar-origin navigation, the old script thread does not update its `WindowProxy`, since the new `Window` is in the new script thread. With this PR, the `WindowProxy` is updated to a dummy `XOriginWindow` object, that only implements the whitelisted methods that are allowed to be called cross-origin.

This PR does not include working implementations of some of the cross-origin `Window` or `Location` methods.

This PR causes some cross-origin wpt tests to now pass, in particular `/html/browsers/origin/cross-origin-objects/cross-origin-objects.html ` now passes `Only whitelisted properties are accessible cross-origin`. There are some CORS failures in `fetch`, I suspect caused by the incorrect setting of `origin` in fetch requests.

Although there are some functions that now throw `SecurityException`, it is not meant to be a complete implementation, which will have to wait for XOWs to land.

---
<!-- 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 #15180.
- [X] There are tests for these changes

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

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/15358)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Feb 17, 2017
2 parents 11396b4 + e8d7655 commit 6adbcb4
Show file tree
Hide file tree
Showing 11 changed files with 598 additions and 16 deletions.
187 changes: 175 additions & 12 deletions components/script/dom/browsingcontext.rs
Expand Up @@ -3,13 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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::proxyhandler::{fill_property_descriptor, get_property_descriptor};
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;
use dom::dissimilaroriginwindow::DissimilarOriginWindow;
use dom::element::Element;
use dom::globalscope::GlobalScope;
use dom::window::Window;
use js::JSCLASS_IS_GLOBAL;
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
Expand All @@ -18,12 +22,13 @@ use js::jsapi::{Handle, HandleId, HandleObject, HandleValue};
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::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById, JS_HasOwnPropertyById};
use js::jsapi::{JS_IsExceptionPending, 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 msg::constellation_msg::PipelineId;
use std::cell::Cell;
use std::ptr;

Expand All @@ -39,6 +44,13 @@ pub struct BrowsingContext {
/// changes Window.
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?
discarded: Cell<bool>,

Expand All @@ -47,9 +59,10 @@ pub struct 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 {
reflector: Reflector::new(),
currently_active: Cell::new(Some(currently_active)),
discarded: Cell::new(false),
frame_element: frame_element.map(JS::from_ref),
}
Expand All @@ -72,7 +85,8 @@ impl BrowsingContext {
assert!(!window_proxy.is_null());

// 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.
// When we finalize the window proxy, it drops the browsing context it owns.
Expand Down Expand Up @@ -104,10 +118,10 @@ impl BrowsingContext {
/// 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) {
fn set_window_proxy(&self, window: &GlobalScope, traps: &ProxyTraps) {
unsafe {
debug!("Setting window proxy of {:p}.", self);
let WindowProxyHandler(handler) = window.windowproxy_handler();
let handler = CreateWrapperProxyHandler(traps);
assert!(!handler.is_null());

let cx = window.get_cx();
Expand Down Expand Up @@ -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 {
let window_proxy = self.reflector.get_jsobject();
assert!(!window_proxy.get().is_null());
Expand Down Expand Up @@ -332,6 +362,145 @@ static PROXY_HANDLER: ProxyTraps = ProxyTraps {
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)]
unsafe extern fn finalize(_fop: *mut JSFreeOp, obj: *mut JSObject) {
let this = GetProxyExtra(obj, 0).to_private() as *mut BrowsingContext;
Expand All @@ -354,9 +523,3 @@ unsafe extern fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
(*this).trace(trc);
}

#[allow(unsafe_code)]
pub fn new_window_proxy_handler() -> WindowProxyHandler {
unsafe {
WindowProxyHandler(CreateWrapperProxyHandler(&PROXY_HANDLER))
}
}
80 changes: 80 additions & 0 deletions components/script/dom/dissimilaroriginlocation.rs
@@ -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)
}
}

0 comments on commit 6adbcb4

Please sign in to comment.