aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorbors-servo <servo-ops@mozilla.com>2021-08-01 10:31:40 -0400
committerGitHub <noreply@github.com>2021-08-01 10:31:40 -0400
commitbd92fad81a24d08208a5739cad4bde6eb58d6ce8 (patch)
treee0e84609080a1d237bfdcaa10e0bef18cdea26ea /components/script
parent052278d0580ec1cbd2c9887e65d259dbf5efa775 (diff)
parentafbe2fa1f259411b6c49a3ec3b2baccfbf1664d9 (diff)
downloadservo-bd92fad81a24d08208a5739cad4bde6eb58d6ce8.tar.gz
servo-bd92fad81a24d08208a5739cad4bde6eb58d6ce8.zip
Auto merge of #28546 - yvt:feat-cow-infra, r=jdm
Implement `Location`'s custom internal methods This PR partly resurrects #16501 and introduces the use of principals object to associate objects and Realms with origins. Using this infrastructure, this PR implements [the custom internal methods][1] of the `Location` interface, which is "maybe-cross-origin". Unimplemented/incomplete things: - Other maybe-cross-origin interfaces, namely `WindowProxy` and `DissimilarWindowLocation`, aren't implemented correctly yet (causing most test cases of `tests/wpt/web-platform-tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html` to fail). - `WindowProxy`: #28556 - [The "perform a security check" operation][2] and `Location`'s non-cross-origin properties' relevant `Document` origin checks aren't implemented either (not sure if they are covered by the existing tests). - There are a slight deviation from the standard and inefficiency in `CrossOriginGetOwnPropertyHelper`'s current implementation. - #28557 [1]: https://html.spec.whatwg.org/multipage/#the-location-interface [2]: https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl --- - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #16243 and make some progress in #2382 --- - [x] There are tests for these changes OR - [ ] These changes do not require tests because ___
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py325
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py8
-rw-r--r--components/script/dom/bindings/interface.rs7
-rw-r--r--components/script/dom/bindings/mod.rs1
-rw-r--r--components/script/dom/bindings/principals.rs139
-rw-r--r--components/script/dom/bindings/proxyhandler.rs605
-rw-r--r--components/script/dom/bindings/utils.rs40
-rw-r--r--components/script/dom/webidls/Location.webidl6
-rw-r--r--components/script/dom/webidls/Window.webidl29
-rw-r--r--components/script/dom/windowproxy.rs3
-rw-r--r--components/script/script_runtime.rs12
-rw-r--r--components/script/script_thread.rs3
12 files changed, 1081 insertions, 97 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 353e690baaf..03998017f36 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -1642,6 +1642,33 @@ class PropertyDefiner:
+ "];\n") % (name, specType)
return specsArray + prefArray
+ def generateUnguardedArray(self, array, name, specTemplate, specTerminator,
+ specType, getCondition, getDataTuple):
+ """
+ Takes the same set of parameters as generateGuardedArray but instead
+ generates a single, flat array of type `&[specType]` that contains all
+ provided members. The provided members' conditions shall be homogeneous,
+ or else this method will fail.
+ """
+
+ # this method can't handle heterogeneous condition
+ groups = groupby(array, lambda m: getCondition(m, self.descriptor))
+ assert len(list(groups)) == 1
+
+ origTemplate = specTemplate
+ if isinstance(specTemplate, str):
+ specTemplate = lambda _: origTemplate # noqa
+
+ specsArray = [specTemplate(m) % getDataTuple(m) for m in array]
+ specsArray.append(specTerminator)
+
+ return dedent(
+ """
+ const %s: &[%s] = &[
+ %s
+ ];
+ """) % (name, specType, ',\n'.join(specsArray))
+
# The length of a method is the minimum of the lengths of the
# argument lists of all its overloads.
@@ -1656,10 +1683,15 @@ class MethodDefiner(PropertyDefiner):
"""
A class for defining methods on a prototype object.
"""
- def __init__(self, descriptor, name, static, unforgeable):
+ def __init__(self, descriptor, name, static, unforgeable, crossorigin=False):
assert not (static and unforgeable)
+ assert not (static and crossorigin)
+ assert not (unforgeable and crossorigin)
PropertyDefiner.__init__(self, descriptor, name)
+ # TODO: Separate the `(static, unforgeable, crossorigin) = (False, False, True)` case
+ # to a separate class or something.
+
# FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
# We should be able to check for special operations without an
# identifier. For now we check if the name starts with __
@@ -1668,14 +1700,15 @@ class MethodDefiner(PropertyDefiner):
if not descriptor.interface.isCallback() or static:
methods = [m for m in descriptor.interface.members if
m.isMethod() and m.isStatic() == static
+ and (bool(m.getExtendedAttribute("CrossOriginCallable")) or not crossorigin)
and not m.isIdentifierLess()
- and MemberIsUnforgeable(m, descriptor) == unforgeable]
+ and (MemberIsUnforgeable(m, descriptor) == unforgeable or crossorigin)]
else:
methods = []
self.regular = [{"name": m.identifier.name,
"methodInfo": not m.isStatic(),
"length": methodLength(m),
- "flags": "JSPROP_ENUMERATE",
+ "flags": "JSPROP_READONLY" if crossorigin else "JSPROP_ENUMERATE",
"condition": PropertyDefiner.getControllingCondition(m, descriptor)}
for m in methods]
@@ -1693,6 +1726,7 @@ class MethodDefiner(PropertyDefiner):
# neither.
if (not static
and not unforgeable
+ and not crossorigin
and descriptor.supportsIndexedProperties()): # noqa
if hasIterator(methods, self.regular): # noqa
raise TypeError("Cannot have indexed getter/attr on "
@@ -1709,7 +1743,7 @@ class MethodDefiner(PropertyDefiner):
# Generate the keys/values/entries aliases for value iterables.
maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
- if (not static and not unforgeable
+ if (not static and not unforgeable and not crossorigin
and maplikeOrSetlikeOrIterable
and maplikeOrSetlikeOrIterable.isIterable()
and maplikeOrSetlikeOrIterable.isValueIterator()):
@@ -1754,7 +1788,7 @@ class MethodDefiner(PropertyDefiner):
})
isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable"))
- if not static and unforgeable == isUnforgeableInterface:
+ if not static and unforgeable == isUnforgeableInterface and not crossorigin:
stringifier = descriptor.operations['Stringifier']
if stringifier:
self.regular.append({
@@ -1765,6 +1799,7 @@ class MethodDefiner(PropertyDefiner):
"condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
})
self.unforgeable = unforgeable
+ self.crossorigin = crossorigin
def generateArray(self, array, name):
if len(array) == 0:
@@ -1796,36 +1831,54 @@ class MethodDefiner(PropertyDefiner):
jitinfo = "ptr::null()"
accessor = 'Some(%s)' % m.get("nativeName", m["name"])
if m["name"].startswith("@@"):
+ assert not self.crossorigin
name = 'JSPropertySpec_Name { symbol_: SymbolCode::%s as usize + 1 }' % m["name"][2:]
else:
name = ('JSPropertySpec_Name { string_: %s as *const u8 as *const libc::c_char }'
% str_to_const_array(m["name"]))
return (name, accessor, jitinfo, m["length"], flags, selfHostedName)
- return self.generateGuardedArray(
- array, name,
+ specTemplate = (
' JSFunctionSpec {\n'
' name: %s,\n'
' call: JSNativeWrapper { op: %s, info: %s },\n'
' nargs: %s,\n'
' flags: (%s) as u16,\n'
' selfHostedName: %s\n'
- ' }',
+ ' }')
+ specTerminator = (
' JSFunctionSpec {\n'
' name: JSPropertySpec_Name { string_: ptr::null() },\n'
' call: JSNativeWrapper { op: None, info: ptr::null() },\n'
' nargs: 0,\n'
' flags: 0,\n'
' selfHostedName: ptr::null()\n'
- ' }',
- 'JSFunctionSpec',
- condition, specData)
+ ' }')
+
+ if self.crossorigin:
+ return self.generateUnguardedArray(
+ array, name,
+ specTemplate, specTerminator,
+ 'JSFunctionSpec',
+ condition, specData)
+ else:
+ return self.generateGuardedArray(
+ array, name,
+ specTemplate, specTerminator,
+ 'JSFunctionSpec',
+ condition, specData)
class AttrDefiner(PropertyDefiner):
- def __init__(self, descriptor, name, static, unforgeable):
+ def __init__(self, descriptor, name, static, unforgeable, crossorigin=False):
assert not (static and unforgeable)
+ assert not (static and crossorigin)
+ assert not (unforgeable and crossorigin)
PropertyDefiner.__init__(self, descriptor, name)
+
+ # TODO: Separate the `(static, unforgeable, crossorigin) = (False, False, True)` case
+ # to a separate class or something.
+
self.name = name
self.descriptor = descriptor
self.regular = [
@@ -1837,12 +1890,16 @@ class AttrDefiner(PropertyDefiner):
}
for m in descriptor.interface.members if
m.isAttr() and m.isStatic() == static
- and MemberIsUnforgeable(m, descriptor) == unforgeable
+ and (MemberIsUnforgeable(m, descriptor) == unforgeable or crossorigin)
+ and (not crossorigin
+ or m.getExtendedAttribute("CrossOriginReadable")
+ or m.getExtendedAttribute("CrossOriginWritable"))
]
self.static = static
self.unforgeable = unforgeable
+ self.crossorigin = crossorigin
- if not static and not unforgeable and not (
+ if not static and not unforgeable and not crossorigin and not (
descriptor.interface.isNamespace() or descriptor.interface.isCallback()
):
self.regular.append({
@@ -1858,6 +1915,10 @@ class AttrDefiner(PropertyDefiner):
def getter(attr):
attr = attr['attr']
+
+ if self.crossorigin and not attr.getExtendedAttribute("CrossOriginReadable"):
+ return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }"
+
if self.static:
accessor = 'get_' + self.descriptor.internalNameFor(attr.identifier.name)
jitinfo = "0 as *const JSJitInfo"
@@ -1874,8 +1935,11 @@ class AttrDefiner(PropertyDefiner):
def setter(attr):
attr = attr['attr']
- if (attr.readonly and not attr.getExtendedAttribute("PutForwards")
- and not attr.getExtendedAttribute("Replaceable")):
+
+ if ((self.crossorigin and not attr.getExtendedAttribute("CrossOriginReadable"))
+ or (attr.readonly
+ and not attr.getExtendedAttribute("PutForwards")
+ and not attr.getExtendedAttribute("Replaceable"))):
return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }"
if self.static:
@@ -1941,12 +2005,20 @@ class AttrDefiner(PropertyDefiner):
}
"""
- return self.generateGuardedArray(
- array, name,
- template,
- ' JSPropertySpec::ZERO',
- 'JSPropertySpec',
- condition, specData)
+ if self.crossorigin:
+ return self.generateUnguardedArray(
+ array, name,
+ template,
+ ' JSPropertySpec::ZERO',
+ 'JSPropertySpec',
+ condition, specData)
+ else:
+ return self.generateGuardedArray(
+ array, name,
+ template,
+ ' JSPropertySpec::ZERO',
+ 'JSPropertySpec',
+ condition, specData)
class ConstDefiner(PropertyDefiner):
@@ -2821,6 +2893,13 @@ class CGWrapMethod(CGAbstractMethod):
def definition_body(self):
unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor)
if self.descriptor.proxy:
+ if self.descriptor.isMaybeCrossOriginObject():
+ proto = "ptr::null_mut()"
+ lazyProto = "true" # Our proxy handler will manage the prototype
+ else:
+ proto = "proto.get()"
+ lazyProto = "false"
+
create = """
let handler: *const libc::c_void =
RegisterBindings::proxy_handlers::%(concreteType)s
@@ -2829,8 +2908,9 @@ rooted!(in(*cx) let obj = NewProxyObject(
*cx,
handler,
Handle::from_raw(UndefinedHandleValue),
- proto.get(),
+ %(proto)s,
ptr::null(),
+ %(lazyProto)s,
));
assert!(!obj.is_null());
SetProxyReservedSlot(
@@ -2839,7 +2919,11 @@ SetProxyReservedSlot(
&PrivateValue(raw.as_ptr() as *const %(concreteType)s as *const libc::c_void),
);
"""
+ create = create % {"concreteType": self.descriptor.concreteType,
+ "proto": proto,
+ "lazyProto": lazyProto}
else:
+ lazyProto = None
create = """
rooted!(in(*cx) let obj = JS_NewObjectWithGivenProto(
*cx,
@@ -2853,7 +2937,7 @@ JS_SetReservedSlot(
&PrivateValue(raw.as_ptr() as *const %(concreteType)s as *const libc::c_void),
);
"""
- create = create % {"concreteType": self.descriptor.concreteType}
+ create = create % {"concreteType": self.descriptor.concreteType}
if self.descriptor.weakReferenceable:
create += """
let val = PrivateValue(ptr::null());
@@ -2912,6 +2996,7 @@ class CGWrapGlobalMethod(CGAbstractMethod):
return CGGeneric("""\
let raw = Root::new(MaybeUnreflectedDom::from_box(object));
+let origin = (*raw.as_ptr()).upcast::<GlobalScope>().origin();
rooted!(in(*cx) let mut obj = ptr::null_mut::<JSObject>());
create_global_object(
@@ -2919,7 +3004,8 @@ create_global_object(
&Class.base,
raw.as_ptr() as *const %(concreteType)s as *const libc::c_void,
_trace,
- obj.handle_mut());
+ obj.handle_mut(),
+ origin);
assert!(!obj.is_null());
let root = raw.reflect_with(obj.get());
@@ -3069,6 +3155,25 @@ class PropertyArrays():
return define
+class CGCrossOriginProperties(CGThing):
+ def __init__(self, descriptor):
+ self.methods = MethodDefiner(descriptor, "CrossOriginMethods", static=False,
+ unforgeable=False, crossorigin=True)
+ self.attributes = AttrDefiner(descriptor, "CrossOriginAttributes", static=False,
+ unforgeable=False, crossorigin=True)
+
+ def define(self):
+ return str(self.methods) + str(self.attributes) + dedent(
+ """
+ const CROSS_ORIGIN_PROPERTIES: proxyhandler::CrossOriginProperties =
+ proxyhandler::CrossOriginProperties {
+ attributes: sCrossOriginAttributes,
+ methods: sCrossOriginMethods,
+ };
+ """
+ )
+
+
class CGCollectJSONAttributesMethod(CGAbstractMethod):
"""
Generate the CollectJSONAttributes method for an interface descriptor
@@ -3449,20 +3554,45 @@ class CGDefineProxyHandler(CGAbstractMethod):
def definition_body(self):
customDefineProperty = 'proxyhandler::define_property'
- if self.descriptor.operations['IndexedSetter'] or self.descriptor.operations['NamedSetter']:
+ if self.descriptor.isMaybeCrossOriginObject() or self.descriptor.operations['IndexedSetter'] or \
+ self.descriptor.operations['NamedSetter']:
customDefineProperty = 'defineProperty'
customDelete = 'proxyhandler::delete'
- if self.descriptor.operations['NamedDeleter']:
+ if self.descriptor.isMaybeCrossOriginObject() or self.descriptor.operations['NamedDeleter']:
customDelete = 'delete'
+ customGetPrototypeIfOrdinary = 'Some(proxyhandler::get_prototype_if_ordinary)'
+ customGetPrototype = 'None'
+ customSetPrototype = 'None'
+ if self.descriptor.isMaybeCrossOriginObject():
+ customGetPrototypeIfOrdinary = 'Some(proxyhandler::maybe_cross_origin_get_prototype_if_ordinary_rawcx)'
+ customGetPrototype = 'Some(getPrototype)'
+ customSetPrototype = 'Some(proxyhandler::maybe_cross_origin_set_prototype_rawcx)'
+ # The base class `BaseProxyHandler`'s `setImmutablePrototype` (not to be
+ # confused with ECMAScript's `[[SetImmutablePrototype]]`) always fails.
+ # This is the desired behavior, so we don't override it.
+
+ customSet = 'None'
+ if self.descriptor.isMaybeCrossOriginObject():
+ # `maybe_cross_origin_set_rawcx` doesn't support legacy platform objects'
+ # `[[Set]]` (https://heycam.github.io/webidl/#legacy-platform-object-set) (yet).
+ assert not self.descriptor.operations['IndexedGetter']
+ assert not self.descriptor.operations['NamedGetter']
+ customSet = 'Some(proxyhandler::maybe_cross_origin_set_rawcx)'
+
getOwnEnumerablePropertyKeys = "own_property_keys"
- if self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ if self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties") or \
+ self.descriptor.isMaybeCrossOriginObject():
getOwnEnumerablePropertyKeys = "getOwnEnumerablePropertyKeys"
args = {
"defineProperty": customDefineProperty,
"delete": customDelete,
+ "getPrototypeIfOrdinary": customGetPrototypeIfOrdinary,
+ "getPrototype": customGetPrototype,
+ "setPrototype": customSetPrototype,
+ "set": customSet,
"getOwnEnumerablePropertyKeys": getOwnEnumerablePropertyKeys,
"trace": TRACE_HOOK_NAME,
"finalize": FINALIZE_HOOK_NAME,
@@ -3476,15 +3606,15 @@ let traps = ProxyTraps {
ownPropertyKeys: Some(own_property_keys),
delete_: Some(%(delete)s),
enumerate: None,
- getPrototypeIfOrdinary: Some(proxyhandler::get_prototype_if_ordinary),
- getPrototype: None,
- setPrototype: None,
+ getPrototypeIfOrdinary: %(getPrototypeIfOrdinary)s,
+ getPrototype: %(getPrototype)s,
+ setPrototype: %(setPrototype)s,
setImmutablePrototype: None,
preventExtensions: Some(proxyhandler::prevent_extensions),
isExtensible: Some(proxyhandler::is_extensible),
has: None,
get: Some(get),
- set: None,
+ set: %(set)s,
call: None,
construct: None,
hasOwn: Some(hasOwn),
@@ -5351,6 +5481,26 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
indexedGetter = self.descriptor.operations['IndexedGetter']
get = "let cx = SafeJSContext::from_ptr(cx);\n"
+
+ if self.descriptor.isMaybeCrossOriginObject():
+ get += dedent(
+ """
+ if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
+ if !proxyhandler::cross_origin_get_own_property_helper(
+ cx, proxy, &CROSS_ORIGIN_PROPERTIES, id, desc
+ ) {
+ return false;
+ }
+ if desc.obj.is_null() {
+ return proxyhandler::cross_origin_property_fallback(cx, proxy, id, desc);
+ }
+ return true;
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+ """)
+
if indexedGetter:
get += "let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
@@ -5448,6 +5598,17 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
def getBody(self):
set = "let cx = SafeJSContext::from_ptr(cx);\n"
+ if self.descriptor.isMaybeCrossOriginObject():
+ set += dedent(
+ """
+ if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
+ return proxyhandler::report_cross_origin_denial(cx, id, "define");
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+ """)
+
indexedSetter = self.descriptor.operations['IndexedSetter']
if indexedSetter:
set += ("let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
@@ -5495,6 +5656,18 @@ class CGDOMJSProxyHandler_delete(CGAbstractExternMethod):
def getBody(self):
set = "let cx = SafeJSContext::from_ptr(cx);\n"
+
+ if self.descriptor.isMaybeCrossOriginObject():
+ set += dedent(
+ """
+ if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
+ return proxyhandler::report_cross_origin_denial(cx, id, "delete");
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+ """)
+
if self.descriptor.operations['NamedDeleter']:
if self.descriptor.hasUnforgeableMembers:
raise TypeError("Can't handle a deleter on an interface that has "
@@ -5522,6 +5695,17 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
let unwrapped_proxy = UnwrapProxy(proxy);
""")
+ if self.descriptor.isMaybeCrossOriginObject():
+ body += dedent(
+ """
+ if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
+ return proxyhandler::cross_origin_own_property_keys(cx, proxy, &CROSS_ORIGIN_PROPERTIES, props);
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+ """)
+
if self.descriptor.operations['IndexedGetter']:
body += dedent(
"""
@@ -5566,7 +5750,8 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod):
def __init__(self, descriptor):
assert (descriptor.operations["IndexedGetter"]
- and descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"))
+ and descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties")
+ or descriptor.isMaybeCrossOriginObject())
args = [Argument('*mut JSContext', 'cx'),
Argument('RawHandleObject', 'proxy'),
Argument('RawMutableHandleIdVector', 'props')]
@@ -5581,6 +5766,18 @@ class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod):
let unwrapped_proxy = UnwrapProxy(proxy);
""")
+ if self.descriptor.isMaybeCrossOriginObject():
+ body += dedent(
+ """
+ if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
+ // There are no enumerable cross-origin props, so we're done.
+ return true;
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+ """)
+
if self.descriptor.operations['IndexedGetter']:
body += dedent(
"""
@@ -5619,6 +5816,18 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
def getBody(self):
indexedGetter = self.descriptor.operations['IndexedGetter']
indexed = "let cx = SafeJSContext::from_ptr(cx);\n"
+
+ if self.descriptor.isMaybeCrossOriginObject():
+ indexed += dedent(
+ """
+ if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
+ return proxyhandler::cross_origin_has_own(cx, proxy, &CROSS_ORIGIN_PROPERTIES, id, bp);
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+ """)
+
if indexedGetter:
indexed += ("let index = get_array_index_from_id(*cx, Handle::from_raw(id));\n"
+ "if let Some(index) = index {\n"
@@ -5680,6 +5889,18 @@ class CGDOMJSProxyHandler_get(CGAbstractExternMethod):
# https://heycam.github.io/webidl/#LegacyPlatformObjectGetOwnProperty
def getBody(self):
+ if self.descriptor.isMaybeCrossOriginObject():
+ maybeCrossOriginGet = dedent(
+ """
+ if !proxyhandler::is_platform_object_same_origin(cx, proxy) {
+ return proxyhandler::cross_origin_get(cx, proxy, receiver, id, vp);
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+ """)
+ else:
+ maybeCrossOriginGet = ""
getFromExpando = """\
rooted!(in(*cx) let mut expando = ptr::null_mut::<JSObject>());
get_expando_object(proxy, expando.handle_mut());
@@ -5735,6 +5956,9 @@ if !expando.is_null() {
//MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
//"Should not have a XrayWrapper here");
let cx = SafeJSContext::from_ptr(cx);
+
+%s
+
let proxy_lt = Handle::from_raw(proxy);
let vp_lt = MutableHandle::from_raw(vp);
let id_lt = Handle::from_raw(id);
@@ -5751,7 +5975,26 @@ if found {
}
%s
vp.set(UndefinedValue());
-return true;""" % (getIndexedOrExpando, getNamed)
+return true;""" % (maybeCrossOriginGet, getIndexedOrExpando, getNamed)
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+
+class CGDOMJSProxyHandler_getPrototype(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'),
+ Argument('RawMutableHandleObject', 'proto')]
+ CGAbstractExternMethod.__init__(self, descriptor, "getPrototype", "bool", args)
+ assert descriptor.isMaybeCrossOriginObject()
+ self.descriptor = descriptor
+
+ def getBody(self):
+ return dedent(
+ """
+ let cx = SafeJSContext::from_ptr(cx);
+ proxyhandler::maybe_cross_origin_get_prototype(cx, proxy, GetProtoObject, proto)
+ """)
def definition_body(self):
return CGGeneric(self.getBody())
@@ -6390,6 +6633,9 @@ class CGDescriptor(CGThing):
if descriptor.proxy:
cgThings.append(CGDefineProxyHandler(descriptor))
+ if descriptor.isMaybeCrossOriginObject():
+ cgThings.append(CGCrossOriginProperties(descriptor))
+
properties = PropertyArrays(descriptor)
if defaultToJSONMethod:
@@ -6401,22 +6647,27 @@ class CGDescriptor(CGThing):
cgThings.append(CGProxyUnwrap(descriptor))
cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
cgThings.append(CGDOMJSProxyHandler_ownPropertyKeys(descriptor))
- if descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
+ if descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties") or \
+ descriptor.isMaybeCrossOriginObject():
cgThings.append(CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(descriptor))
cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor))
cgThings.append(CGDOMJSProxyHandler_className(descriptor))
cgThings.append(CGDOMJSProxyHandler_get(descriptor))
cgThings.append(CGDOMJSProxyHandler_hasOwn(descriptor))
- if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
+ if descriptor.isMaybeCrossOriginObject() or descriptor.operations['IndexedSetter'] or \
+ descriptor.operations['NamedSetter']:
cgThings.append(CGDOMJSProxyHandler_defineProperty(descriptor))
# We want to prevent indexed deleters from compiling at all.
assert not descriptor.operations['IndexedDeleter']
- if descriptor.operations['NamedDeleter']:
+ if descriptor.isMaybeCrossOriginObject() or descriptor.operations['NamedDeleter']:
cgThings.append(CGDOMJSProxyHandler_delete(descriptor))
+ if descriptor.isMaybeCrossOriginObject():
+ cgThings.append(CGDOMJSProxyHandler_getPrototype(descriptor))
+
# cgThings.append(CGDOMJSProxyHandler(descriptor))
# cgThings.append(CGIsMethod(descriptor))
pass
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
index b92f68af3b9..cf6885be265 100644
--- a/components/script/dom/bindings/codegen/Configuration.py
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -299,6 +299,9 @@ class Descriptor(DescriptorProvider):
if iface:
iface.setUserData('hasConcreteDescendant', True)
+ if self.isMaybeCrossOriginObject():
+ self.proxy = True
+
if self.proxy:
iface = self.interface
while iface.parent:
@@ -404,6 +407,11 @@ class Descriptor(DescriptorProvider):
def supportsIndexedProperties(self):
return self.operations['IndexedGetter'] is not None
+ def isMaybeCrossOriginObject(self):
+ # If we're isGlobal and have cross-origin members, we're a Window, and
+ # that's not a cross-origin object. The WindowProxy is.
+ return self.concrete and self.interface.hasCrossOriginMembers and not self.isGlobal()
+
def hasDescendants(self):
return (self.interface.getUserData("hasConcreteDescendant", False)
or self.interface.getUserData("hasProxyDescendant", False))
diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs
index 82303f800ba..f73152c9df7 100644
--- a/components/script/dom/bindings/interface.rs
+++ b/components/script/dom/bindings/interface.rs
@@ -9,6 +9,7 @@ use crate::dom::bindings::codegen::PrototypeList;
use crate::dom::bindings::constant::{define_constants, ConstantSpec};
use crate::dom::bindings::conversions::{get_dom_class, DOM_OBJECT_SLOT};
use crate::dom::bindings::guard::Guard;
+use crate::dom::bindings::principals::ServoJSPrincipals;
use crate::dom::bindings::utils::{ProtoOrIfaceArray, DOM_PROTOTYPE_SLOT};
use crate::script_runtime::JSContext as SafeJSContext;
use js::error::throw_type_error;
@@ -36,6 +37,7 @@ use js::rust::wrappers::{JS_DefineProperty3, JS_DefineProperty4, JS_DefineProper
use js::rust::wrappers::{JS_LinkConstructorAndPrototype, JS_NewObjectWithGivenProto};
use js::rust::{define_methods, define_properties, get_object_class};
use js::rust::{HandleObject, HandleValue, MutableHandleObject, RealmOptions};
+use servo_url::MutableOrigin;
use std::convert::TryFrom;
use std::ptr;
@@ -136,6 +138,7 @@ pub unsafe fn create_global_object(
private: *const libc::c_void,
trace: TraceHook,
mut rval: MutableHandleObject,
+ origin: &MutableOrigin,
) {
assert!(rval.is_null());
@@ -145,10 +148,12 @@ pub unsafe fn create_global_object(
options.creationOptions_.streams_ = true;
select_compartment(cx, &mut options);
+ let principal = ServoJSPrincipals::new(origin);
+
rval.set(JS_NewGlobalObject(
*cx,
class,
- ptr::null_mut(),
+ principal.as_raw(),
OnNewGlobalHookOption::DontFireOnNewGlobalHook,
&*options,
));
diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs
index 8b3f9682544..e446af3693f 100644
--- a/components/script/dom/bindings/mod.rs
+++ b/components/script/dom/bindings/mod.rs
@@ -145,6 +145,7 @@ pub mod interface;
pub mod iterable;
pub mod namespace;
pub mod num;
+pub mod principals;
pub mod proxyhandler;
pub mod record;
pub mod refcounted;
diff --git a/components/script/dom/bindings/principals.rs b/components/script/dom/bindings/principals.rs
new file mode 100644
index 00000000000..b16bc8859db
--- /dev/null
+++ b/components/script/dom/bindings/principals.rs
@@ -0,0 +1,139 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use js::{
+ glue::{
+ CreateRustJSPrincipals, DestroyRustJSPrincipals, GetRustJSPrincipalsPrivate,
+ JSPrincipalsCallbacks,
+ },
+ jsapi::{JSPrincipals, JS_DropPrincipals, JS_HoldPrincipals},
+ rust::Runtime,
+};
+use servo_url::MutableOrigin;
+use std::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr::NonNull};
+
+/// An owned reference to Servo's `JSPrincipals` instance.
+#[repr(transparent)]
+pub struct ServoJSPrincipals(NonNull<JSPrincipals>);
+
+impl ServoJSPrincipals {
+ pub fn new(origin: &MutableOrigin) -> Self {
+ unsafe {
+ let private: Box<MutableOrigin> = Box::new(origin.clone());
+ let raw = CreateRustJSPrincipals(&PRINCIPALS_CALLBACKS, Box::into_raw(private) as _);
+ // The created `JSPrincipals` object has an initial reference
+ // count of zero, so the following code will set it to one
+ Self::from_raw_nonnull(NonNull::new_unchecked(raw))
+ }
+ }
+
+ /// Construct `Self` from a raw `*mut JSPrincipals`, incrementing its
+ /// reference count.
+ #[inline]
+ pub unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self {
+ JS_HoldPrincipals(raw.as_ptr());
+ Self(raw)
+ }
+
+ #[inline]
+ pub unsafe fn origin(&self) -> MutableOrigin {
+ let origin = GetRustJSPrincipalsPrivate(self.0.as_ptr()) as *mut MutableOrigin;
+ (*origin).clone()
+ }
+
+ #[inline]
+ pub fn as_raw_nonnull(&self) -> NonNull<JSPrincipals> {
+ self.0
+ }
+
+ #[inline]
+ pub fn as_raw(&self) -> *mut JSPrincipals {
+ self.0.as_ptr()
+ }
+}
+
+impl Clone for ServoJSPrincipals {
+ #[inline]
+ fn clone(&self) -> Self {
+ unsafe { Self::from_raw_nonnull(self.as_raw_nonnull()) }
+ }
+}
+
+impl Drop for ServoJSPrincipals {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe { JS_DropPrincipals(Runtime::get(), self.as_raw()) };
+ }
+}
+
+/// A borrowed reference to Servo's `JSPrincipals` instance. Does not update the
+/// reference count on creation and deletion.
+pub struct ServoJSPrincipalsRef<'a>(ManuallyDrop<ServoJSPrincipals>, PhantomData<&'a ()>);
+
+impl ServoJSPrincipalsRef<'_> {
+ /// Construct `Self` from a raw `NonNull<JSPrincipals>`.
+ ///
+ /// # Safety
+ ///
+ /// `ServoJSPrincipalsRef` does not update the reference count of the
+ /// wrapped `JSPrincipals` object. It's up to the caller to ensure the
+ /// returned `ServoJSPrincipalsRef` object or any clones are not used past
+ /// the lifetime of the wrapped object.
+ #[inline]
+ pub unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self {
+ // Don't use `ServoJSPrincipals::from_raw_nonnull`; we don't want to
+ // update the reference count
+ Self(ManuallyDrop::new(ServoJSPrincipals(raw)), PhantomData)
+ }
+
+ /// Construct `Self` from a raw `*mut JSPrincipals`.
+ ///
+ /// # Safety
+ ///
+ /// The behavior is undefined if `raw` is null. See also
+ /// [`Self::from_raw_nonnull`].
+ #[inline]
+ pub unsafe fn from_raw_unchecked(raw: *mut JSPrincipals) -> Self {
+ Self::from_raw_nonnull(NonNull::new_unchecked(raw))
+ }
+}
+
+impl Clone for ServoJSPrincipalsRef<'_> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Self(ManuallyDrop::new(ServoJSPrincipals(self.0 .0)), PhantomData)
+ }
+}
+
+impl Deref for ServoJSPrincipalsRef<'_> {
+ type Target = ServoJSPrincipals;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+pub unsafe extern "C" fn destroy_servo_jsprincipal(principals: *mut JSPrincipals) {
+ Box::from_raw(GetRustJSPrincipalsPrivate(principals) as *mut MutableOrigin);
+ DestroyRustJSPrincipals(principals);
+}
+
+const PRINCIPALS_CALLBACKS: JSPrincipalsCallbacks = JSPrincipalsCallbacks {
+ write: None,
+ isSystemOrAddonPrincipal: Some(principals_is_system_or_addon_principal),
+};
+
+unsafe extern "C" fn principals_is_system_or_addon_principal(_: *mut JSPrincipals) -> bool {
+ false
+}
+
+//TODO is same_origin_domain equivalent to subsumes for our purposes
+pub unsafe extern "C" fn subsumes(obj: *mut JSPrincipals, other: *mut JSPrincipals) -> bool {
+ let obj = ServoJSPrincipalsRef::from_raw_unchecked(obj);
+ let other = ServoJSPrincipalsRef::from_raw_unchecked(other);
+ let obj_origin = obj.origin();
+ let other_origin = other.origin();
+ obj_origin.same_origin_domain(&other_origin)
+}
diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs
index 11913c03642..3650df6186b 100644
--- a/components/script/dom/bindings/proxyhandler.rs
+++ b/components/script/dom/bindings/proxyhandler.rs
@@ -6,25 +6,47 @@
#![deny(missing_docs)]
-use crate::dom::bindings::conversions::is_dom_proxy;
+use crate::dom::bindings::conversions::{is_dom_proxy, jsid_to_string, jsstring_to_str};
+use crate::dom::bindings::error::{throw_dom_exception, Error};
+use crate::dom::bindings::principals::ServoJSPrincipalsRef;
+use crate::dom::bindings::reflector::DomObject;
+use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::utils::delete_property_by_id;
-use js::glue::GetProxyHandlerFamily;
+use crate::dom::globalscope::GlobalScope;
+use crate::realms::{AlreadyInRealm, InRealm};
+use crate::script_runtime::JSContext as SafeJSContext;
+use js::conversions::ToJSValConvertible;
+use js::glue::{
+ GetProxyHandler, GetProxyHandlerFamily, InvokeGetOwnPropertyDescriptor, RUST_SYMBOL_TO_JSID,
+};
use js::glue::{GetProxyPrivate, SetProxyPrivate};
+use js::jsapi;
use js::jsapi::GetStaticPrototype;
use js::jsapi::Handle as RawHandle;
use js::jsapi::HandleId as RawHandleId;
use js::jsapi::HandleObject as RawHandleObject;
+use js::jsapi::HandleValue as RawHandleValue;
+use js::jsapi::JSAutoRealm;
+use js::jsapi::JS_AtomizeAndPinString;
use js::jsapi::JS_DefinePropertyById;
+use js::jsapi::JS_GetOwnPropertyDescriptorById;
+use js::jsapi::JS_IsExceptionPending;
+use js::jsapi::MutableHandle as RawMutableHandle;
+use js::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
use js::jsapi::MutableHandleObject as RawMutableHandleObject;
+use js::jsapi::MutableHandleValue as RawMutableHandleValue;
use js::jsapi::ObjectOpResult;
+use js::jsapi::{jsid, GetObjectRealmOrNull, GetRealmPrincipals, JSFunctionSpec, JSPropertySpec};
use js::jsapi::{DOMProxyShadowsResult, JSContext, JSObject, PropertyDescriptor};
+use js::jsapi::{GetWellKnownSymbol, SymbolCode};
use js::jsapi::{JSErrNum, SetDOMProxyInformation};
use js::jsval::ObjectValue;
use js::jsval::UndefinedValue;
use js::rust::wrappers::JS_AlreadyHasOwnPropertyById;
use js::rust::wrappers::JS_NewObjectWithGivenProto;
-use js::rust::{Handle, HandleObject, MutableHandle, MutableHandleObject};
-use std::ptr;
+use js::rust::wrappers::{AppendToIdVector, RUST_INTERNED_STRING_TO_JSID};
+use js::rust::{get_context_realm, Handle, HandleObject, MutableHandle, MutableHandleObject};
+use std::{ffi::CStr, os::raw::c_char, ptr};
/// Determine if this id shadows any existing properties for this proxy.
pub unsafe extern "C" fn shadow_check_callback(
@@ -120,7 +142,7 @@ pub unsafe extern "C" fn is_extensible(
///
/// This implementation always handles the case of the ordinary
/// `[[GetPrototypeOf]]` behavior. An alternative implementation will be
-/// necessary for the Location object.
+/// necessary for maybe-cross-origin objects.
pub unsafe extern "C" fn get_prototype_if_ordinary(
_: *mut JSContext,
proxy: RawHandleObject,
@@ -177,3 +199,576 @@ pub fn fill_property_descriptor(
desc.getter = None;
desc.setter = None;
}
+
+/// <https://html.spec.whatwg.org/multipage/#isplatformobjectsameorigin-(-o-)>
+pub unsafe fn is_platform_object_same_origin(cx: SafeJSContext, obj: RawHandleObject) -> bool {
+ let subject_realm = get_context_realm(*cx);
+ let obj_realm = GetObjectRealmOrNull(*obj);
+ assert!(!obj_realm.is_null());
+
+ let subject_principals =
+ ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(subject_realm));
+ let obj_principals = ServoJSPrincipalsRef::from_raw_unchecked(GetRealmPrincipals(obj_realm));
+
+ let subject_origin = subject_principals.origin();
+ let obj_origin = obj_principals.origin();
+
+ let result = subject_origin.same_origin_domain(&obj_origin);
+ log::trace!(
+ "object {:p} (realm = {:p}, principalls = {:p}, origin = {:?}) is {} \
+ with reference to the current Realm (realm = {:p}, principals = {:p}, \
+ origin = {:?})",
+ obj.get(),
+ obj_realm,
+ obj_principals.as_raw(),
+ obj_origin.immutable(),
+ ["NOT same domain-origin", "same domain-origin"][result as usize],
+ subject_realm,
+ subject_principals.as_raw(),
+ subject_origin.immutable()
+ );
+
+ result
+}
+
+/// Report a cross-origin denial for a property, Always returns `false`, so it
+/// can be used as `return report_cross_origin_denial(...);`.
+///
+/// What this function does corresponds to the operations in
+/// <https://html.spec.whatwg.org/multipage/#the-location-interface> denoted as
+/// "Throw a `SecurityError` DOMException".
+pub unsafe fn report_cross_origin_denial(cx: SafeJSContext, id: RawHandleId, access: &str) -> bool {
+ debug!(
+ "permission denied to {} property {} on cross-origin object",
+ access,
+ id_to_source(cx, id).as_deref().unwrap_or("< error >"),
+ );
+ let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
+ if !JS_IsExceptionPending(*cx) {
+ let global = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
+ // TODO: include `id` and `access` in the exception message
+ throw_dom_exception(cx, &*global, Error::Security);
+ }
+ false
+}
+
+unsafe fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> {
+ rooted!(in(*cx) let mut value = UndefinedValue());
+ rooted!(in(*cx) let mut jsstr = ptr::null_mut::<jsapi::JSString>());
+ jsapi::JS_IdToValue(*cx, id.get(), value.handle_mut().into())
+ .then(|| {
+ jsstr.set(jsapi::JS_ValueToSource(*cx, value.handle().into()));
+ jsstr.get()
+ })
+ .filter(|jsstr| !jsstr.is_null())
+ .map(|jsstr| jsstring_to_str(*cx, jsstr))
+}
+
+/// Property and method specs that correspond to the elements of
+/// [`CrossOriginProperties(O)`].
+///
+/// [`CrossOriginProperties(O)`]: https://html.spec.whatwg.org/multipage/#crossoriginproperties-(-o-)
+pub struct CrossOriginProperties {
+ pub attributes: &'static [JSPropertySpec],
+ pub methods: &'static [JSFunctionSpec],
+}
+
+impl CrossOriginProperties {
+ /// Enumerate the property keys defined by `self`.
+ fn keys(&self) -> impl Iterator<Item = *const c_char> + '_ {
+ // Safety: All cross-origin property keys are strings, not symbols
+ self.attributes
+ .iter()
+ .map(|spec| unsafe { spec.name.string_ })
+ .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ }))
+ .filter(|ptr| !ptr.is_null())
+ }
+}
+
+/// Implementation of [`CrossOriginOwnPropertyKeys`].
+///
+/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-)
+pub unsafe fn cross_origin_own_property_keys(
+ cx: SafeJSContext,
+ _proxy: RawHandleObject,
+ cross_origin_properties: &'static CrossOriginProperties,
+ props: RawMutableHandleIdVector,
+) -> bool {
+ // > 2. For each `e` of `! CrossOriginProperties(O)`, append
+ // > `e.[[Property]]` to `keys`.
+ for key in cross_origin_properties.keys() {
+ rooted!(in(*cx) let rooted = JS_AtomizeAndPinString(*cx, key));
+ rooted!(in(*cx) let mut rooted_jsid: jsid);
+ RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), rooted_jsid.handle_mut());
+ AppendToIdVector(props, rooted_jsid.handle());
+ }
+
+ // > 3. Return the concatenation of `keys` and `« "then", @@toStringTag,
+ // > @@hasInstance, @@isConcatSpreadable »`.
+ append_cross_origin_allowlisted_prop_keys(cx, props);
+
+ true
+}
+
+/// Implementation of `[[Set]]` for [`Location`].
+///
+/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-set
+pub unsafe extern "C" fn maybe_cross_origin_set_rawcx(
+ cx: *mut JSContext,
+ proxy: RawHandleObject,
+ id: RawHandleId,
+ v: RawHandleValue,
+ receiver: RawHandleValue,
+ result: *mut ObjectOpResult,
+) -> bool {
+ let cx = SafeJSContext::from_ptr(cx);
+
+ if !is_platform_object_same_origin(cx, proxy) {
+ return cross_origin_set(cx, proxy, id, v, receiver, result);
+ }
+
+ // Safe to enter the Realm of proxy now.
+ let _ac = JSAutoRealm::new(*cx, proxy.get());
+
+ // OrdinarySet
+ // <https://tc39.es/ecma262/#sec-ordinaryset>
+ rooted!(in(*cx) let mut own_desc = PropertyDescriptor::default());
+ if !InvokeGetOwnPropertyDescriptor(
+ GetProxyHandler(*proxy),
+ *cx,
+ proxy,
+ id,
+ own_desc.handle_mut().into(),
+ ) {
+ return false;
+ }
+
+ js::jsapi::SetPropertyIgnoringNamedGetter(
+ *cx,
+ proxy,
+ id,
+ v,
+ receiver,
+ own_desc.handle().into(),
+ result,
+ )
+}
+
+pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx(
+ _: *mut JSContext,
+ _proxy: RawHandleObject,
+ is_ordinary: *mut bool,
+ _proto: RawMutableHandleObject,
+) -> bool {
+ // We have a custom `[[GetPrototypeOf]]`, so return `false`
+ *is_ordinary = false;
+ true
+}
+
+/// Implementation of `[[GetPrototypeOf]]` for [`Location`].
+///
+/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-getprototypeof
+pub unsafe fn maybe_cross_origin_get_prototype(
+ cx: SafeJSContext,
+ proxy: RawHandleObject,
+ get_proto_object: unsafe fn(cx: SafeJSContext, global: HandleObject, rval: MutableHandleObject),
+ proto: RawMutableHandleObject,
+) -> bool {
+ // > 1. If ! IsPlatformObjectSameOrigin(this) is true, then return ! OrdinaryGetPrototypeOf(this).
+ if is_platform_object_same_origin(cx, proxy) {
+ let ac = JSAutoRealm::new(*cx, proxy.get());
+ let global = GlobalScope::from_context(*cx, InRealm::Entered(&ac));
+ get_proto_object(
+ cx,
+ global.reflector().get_jsobject(),
+ MutableHandleObject::from_raw(proto),
+ );
+ return !proto.is_null();
+ }
+
+ // > 2. Return null.
+ proto.set(ptr::null_mut());
+ true
+}
+
+/// Implementation of `[[SetPrototypeOf]]` for [`Location`] and [`WindowProxy`].
+///
+/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-setprototypeof
+/// [`WindowProxy`]: https://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof
+pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx(
+ cx: *mut JSContext,
+ proxy: RawHandleObject,
+ proto: RawHandleObject,
+ result: *mut ObjectOpResult,
+) -> bool {
+ // > 1. Return `! SetImmutablePrototype(this, V)`.
+ //
+ // <https://tc39.es/ecma262/#sec-set-immutable-prototype>:
+ //
+ // > 1. Assert: Either `Type(V)` is Object or `Type(V)` is Null.
+ //
+ // > 2. Let current be `? O.[[GetPrototypeOf]]()`.
+ rooted!(in(cx) let mut current = ptr::null_mut::<JSObject>());
+ if !jsapi::GetObjectProto(cx, proxy, current.handle_mut().into()) {
+ return false;
+ }
+
+ // > 3. If `SameValue(V, current)` is true, return true.
+ if proto.get() == current.get() {
+ (*result).code_ = 0 /* OkCode */;
+ return true;
+ }
+
+ // > 4. Return false.
+ (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize;
+ true
+}
+
+/// Implementation of [`CrossOriginGet`].
+///
+/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
+/// for a maybe-cross-origin object.
+///
+/// [`CrossOriginGet`]: https://html.spec.whatwg.org/multipage/#crossoriginget-(-o,-p,-receiver-)
+pub unsafe fn cross_origin_get(
+ cx: SafeJSContext,
+ proxy: RawHandleObject,
+ receiver: RawHandleValue,
+ id: RawHandleId,
+ vp: RawMutableHandleValue,
+) -> bool {
+ // > 1. Let `desc` be `? O.[[GetOwnProperty]](P)`.
+ rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default());
+ if !InvokeGetOwnPropertyDescriptor(
+ GetProxyHandler(*proxy),
+ *cx,
+ proxy,
+ id,
+ descriptor.handle_mut().into(),
+ ) {
+ return false;
+ }
+
+ // > 2. Assert: `desc` is not undefined.
+ assert!(
+ !descriptor.obj.is_null(),
+ "Callees should throw in all cases when they are not finding \
+ a property decriptor"
+ );
+
+ // > 3. If `! IsDataDescriptor(desc)` is true, then return `desc.[[Value]]`.
+ if is_data_descriptor(&descriptor) {
+ vp.set(descriptor.value);
+ return true;
+ }
+
+ // > 4. Assert: `IsAccessorDescriptor(desc)` is `true`.
+ assert!(is_accessor_descriptor(&descriptor));
+
+ // > 5. Let `getter` be `desc.[[Get]]`.
+ // >
+ // > 6. If `getter` is `undefined`, then throw a `SecurityError`
+ // > `DOMException`.
+ rooted!(in(*cx) let mut getter = ptr::null_mut::<JSObject>());
+ get_getter_object(&descriptor, getter.handle_mut().into());
+ if getter.get().is_null() {
+ return report_cross_origin_denial(cx, id, "get");
+ }
+
+ rooted!(in(*cx) let mut getter_jsval = UndefinedValue());
+ getter.get().to_jsval(*cx, getter_jsval.handle_mut());
+
+ // > 7. Return `? Call(getter, Receiver)`.
+ jsapi::Call(
+ *cx,
+ receiver,
+ getter_jsval.handle().into(),
+ &jsapi::HandleValueArray::new(),
+ vp,
+ )
+}
+
+/// Implementation of [`CrossOriginSet`].
+///
+/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
+/// for a maybe-cross-origin object.
+///
+/// [`CrossOriginSet`]: https://html.spec.whatwg.org/multipage/#crossoriginset-(-o,-p,-v,-receiver-)
+pub unsafe fn cross_origin_set(
+ cx: SafeJSContext,
+ proxy: RawHandleObject,
+ id: RawHandleId,
+ v: RawHandleValue,
+ receiver: RawHandleValue,
+ result: *mut ObjectOpResult,
+) -> bool {
+ // > 1. Let desc be ? O.[[GetOwnProperty]](P).
+ rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default());
+ if !InvokeGetOwnPropertyDescriptor(
+ GetProxyHandler(*proxy),
+ *cx,
+ proxy,
+ id,
+ descriptor.handle_mut().into(),
+ ) {
+ return false;
+ }
+
+ // > 2. Assert: desc is not undefined.
+ assert!(
+ !descriptor.obj.is_null(),
+ "Callees should throw in all cases when they are not finding \
+ a property decriptor"
+ );
+
+ // > 3. If desc.[[Set]] is present and its value is not undefined,
+ // > then: [...]
+ rooted!(in(*cx) let mut setter = ptr::null_mut::<JSObject>());
+ get_setter_object(&descriptor, setter.handle_mut().into());
+ if setter.get().is_null() {
+ // > 4. Throw a "SecurityError" DOMException.
+ return report_cross_origin_denial(cx, id, "set");
+ }
+
+ rooted!(in(*cx) let mut setter_jsval = UndefinedValue());
+ setter.get().to_jsval(*cx, setter_jsval.handle_mut());
+
+ // > 3.1. Perform ? Call(setter, Receiver, «V»).
+ // >
+ // > 3.2. Return true.
+ rooted!(in(*cx) let mut ignored = UndefinedValue());
+ if !jsapi::Call(
+ *cx,
+ receiver,
+ setter_jsval.handle().into(),
+ // FIXME: Our binding lacks `HandleValueArray(Handle<Value>)`
+ // <https://searchfox.org/mozilla-central/rev/072710086ddfe25aa2962c8399fefb2304e8193b/js/public/ValueArray.h#54-55>
+ &jsapi::HandleValueArray {
+ length_: 1,
+ elements_: v.ptr,
+ },
+ ignored.handle_mut().into(),
+ ) {
+ return false;
+ }
+
+ (*result).code_ = 0 /* OkCode */;
+ true
+}
+
+unsafe fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
+ if (d.attrs & jsapi::JSPROP_GETTER as u32) != 0 {
+ out.set(std::mem::transmute(d.getter));
+ }
+}
+
+unsafe fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) {
+ if (d.attrs & jsapi::JSPROP_SETTER as u32) != 0 {
+ out.set(std::mem::transmute(d.setter));
+ }
+}
+
+/// <https://tc39.es/ecma262/#sec-isaccessordescriptor>
+fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool {
+ d.attrs & (jsapi::JSPROP_GETTER as u32 | jsapi::JSPROP_SETTER as u32) != 0
+}
+
+/// <https://tc39.es/ecma262/#sec-isdatadescriptor>
+fn is_data_descriptor(d: &PropertyDescriptor) -> bool {
+ let is_accessor = is_accessor_descriptor(d);
+ let is_generic = d.attrs &
+ (jsapi::JSPROP_GETTER as u32 |
+ jsapi::JSPROP_SETTER as u32 |
+ jsapi::JSPROP_IGNORE_READONLY |
+ jsapi::JSPROP_IGNORE_VALUE) ==
+ jsapi::JSPROP_IGNORE_READONLY | jsapi::JSPROP_IGNORE_VALUE;
+ !is_accessor && !is_generic
+}
+
+/// Evaluate `CrossOriginGetOwnPropertyHelper(proxy, id) != null`.
+/// SpiderMonkey-specific.
+///
+/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
+/// for a maybe-cross-origin object.
+pub unsafe fn cross_origin_has_own(
+ cx: SafeJSContext,
+ _proxy: RawHandleObject,
+ cross_origin_properties: &'static CrossOriginProperties,
+ id: RawHandleId,
+ bp: *mut bool,
+) -> bool {
+ // TODO: Once we have the slot for the holder, it'd be more efficient to
+ // use `ensure_cross_origin_property_holder`. We'll need `_proxy` to
+ // do that.
+ *bp = jsid_to_string(*cx, Handle::from_raw(id)).map_or(false, |key| {
+ cross_origin_properties.keys().any(|defined_key| {
+ let defined_key = CStr::from_ptr(defined_key);
+ defined_key.to_bytes() == key.as_bytes()
+ })
+ });
+
+ true
+}
+
+/// Implementation of [`CrossOriginGetOwnPropertyHelper`].
+///
+/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
+/// for a maybe-cross-origin object.
+///
+/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-)
+pub unsafe fn cross_origin_get_own_property_helper(
+ cx: SafeJSContext,
+ proxy: RawHandleObject,
+ cross_origin_properties: &'static CrossOriginProperties,
+ id: RawHandleId,
+ mut desc: RawMutableHandle<PropertyDescriptor>,
+) -> bool {
+ rooted!(in(*cx) let mut holder = ptr::null_mut::<JSObject>());
+
+ ensure_cross_origin_property_holder(
+ cx,
+ proxy,
+ cross_origin_properties,
+ holder.handle_mut().into(),
+ );
+
+ if !JS_GetOwnPropertyDescriptorById(*cx, holder.handle().into(), id, desc) {
+ return false;
+ }
+
+ if !desc.obj.is_null() {
+ desc.obj = proxy.get();
+ }
+
+ true
+}
+
+/// Implementation of [`CrossOriginPropertyFallback`].
+///
+/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
+/// for a maybe-cross-origin object.
+///
+/// [`CrossOriginPropertyFallback`]: https://html.spec.whatwg.org/multipage/#crossoriginpropertyfallback-(-p-)
+pub unsafe fn cross_origin_property_fallback(
+ cx: SafeJSContext,
+ proxy: RawHandleObject,
+ id: RawHandleId,
+ mut desc: RawMutableHandle<PropertyDescriptor>,
+) -> bool {
+ assert!(desc.obj.is_null(), "why are we being called?");
+
+ // > 1. If P is `then`, `@@toStringTag`, `@@hasInstance`, or
+ // > `@@isConcatSpreadable`, then return `PropertyDescriptor{ [[Value]]:
+ // > undefined, [[Writable]]: false, [[Enumerable]]: false,
+ // > [[Configurable]]: true }`.
+ if is_cross_origin_allowlisted_prop(cx, id) {
+ *desc = PropertyDescriptor {
+ getter: None,
+ setter: None,
+ value: UndefinedValue(),
+ attrs: jsapi::JSPROP_READONLY as u32,
+ obj: proxy.get(),
+ };
+ return true;
+ }
+
+ // > 2. Throw a `SecurityError` `DOMException`.
+ report_cross_origin_denial(cx, id, "access")
+}
+
+const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[
+ SymbolCode::toStringTag,
+ SymbolCode::hasInstance,
+ SymbolCode::isConcatSpreadable,
+];
+
+unsafe fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool {
+ if jsid_to_string(*cx, Handle::from_raw(id)).map_or(false, |st| st == "then") {
+ return true;
+ }
+
+ rooted!(in(*cx) let mut allowed_id: jsid);
+ ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| {
+ RUST_SYMBOL_TO_JSID(
+ GetWellKnownSymbol(*cx, allowed_code),
+ allowed_id.handle_mut().into(),
+ );
+ // `jsid`s containing `JS::Symbol *` can be compared by
+ // referential equality
+ allowed_id.get().asBits == id.asBits
+ })
+}
+
+/// Append `« "then", @@toStringTag, @@hasInstance, @@isConcatSpreadable »` to
+/// `props`. This is used to implement [`CrossOriginOwnPropertyKeys`].
+///
+/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-)
+unsafe fn append_cross_origin_allowlisted_prop_keys(
+ cx: SafeJSContext,
+ props: RawMutableHandleIdVector,
+) {
+ rooted!(in(*cx) let mut id: jsid);
+
+ let jsstring = JS_AtomizeAndPinString(*cx, b"then\0".as_ptr() as *const c_char);
+ rooted!(in(*cx) let rooted = jsstring);
+ RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), id.handle_mut());
+ AppendToIdVector(props, id.handle());
+
+ for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() {
+ RUST_SYMBOL_TO_JSID(
+ GetWellKnownSymbol(*cx, allowed_code),
+ id.handle_mut().into(),
+ );
+ AppendToIdVector(props, id.handle());
+ }
+}
+
+/// Get the holder for cross-origin properties for the current global of the
+/// `JSContext`, creating one and storing it in a slot of the proxy object if it
+/// doesn't exist yet.
+///
+/// This essentially creates a cache of [`CrossOriginGetOwnPropertyHelper`]'s
+/// results for all property keys.
+///
+/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy
+/// for a maybe-cross-origin object. The `out_holder` return value will always
+/// be in the Realm of `cx`.
+///
+/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-)
+unsafe fn ensure_cross_origin_property_holder(
+ cx: SafeJSContext,
+ _proxy: RawHandleObject,
+ cross_origin_properties: &'static CrossOriginProperties,
+ out_holder: RawMutableHandleObject,
+) -> bool {
+ // TODO: We don't have the slot to store the holder yet. For now,
+ // the holder is constructed every time this function is called,
+ // which is not only inefficient but also deviates from the
+ // specification in a subtle yet observable way.
+
+ // Create a holder for the current Realm
+ out_holder.set(jsapi::JS_NewObjectWithGivenProto(
+ *cx,
+ ptr::null_mut(),
+ RawHandleObject::null(),
+ ));
+
+ if out_holder.get().is_null() ||
+ !jsapi::JS_DefineProperties(
+ *cx,
+ out_holder.handle(),
+ cross_origin_properties.attributes.as_ptr(),
+ ) ||
+ !jsapi::JS_DefineFunctions(
+ *cx,
+ out_holder.handle(),
+ cross_origin_properties.methods.as_ptr(),
+ )
+ {
+ return false;
+ }
+
+ // TODO: Store the holder in the slot that we don't have yet.
+
+ true
+}
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index 36f255bc4ee..77192291416 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -17,8 +17,8 @@ use crate::dom::bindings::trace::trace_object;
use crate::dom::windowproxy;
use crate::script_runtime::JSContext as SafeJSContext;
use js::conversions::ToJSValConvertible;
+use js::glue::JS_GetReservedSlot;
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
-use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew};
use js::glue::{UnwrapObjectDynamic, UnwrapObjectStatic, RUST_JSID_TO_INT, RUST_JSID_TO_STRING};
use js::glue::{
RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_STRING, RUST_JSID_IS_VOID,
@@ -26,12 +26,11 @@ use js::glue::{
use js::jsapi::HandleId as RawHandleId;
use js::jsapi::HandleObject as RawHandleObject;
use js::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
-use js::jsapi::MutableHandleObject as RawMutableHandleObject;
use js::jsapi::{AtomToLinearString, GetLinearStringCharAt, GetLinearStringLength};
use js::jsapi::{CallArgs, DOMCallbacks, GetNonCCWObjectGlobal};
-use js::jsapi::{Heap, JSAutoRealm, JSContext, JS_FreezeObject};
+use js::jsapi::{Heap, JSContext, JS_FreezeObject};
use js::jsapi::{JSAtom, JS_IsExceptionPending, JS_IsGlobalObject};
-use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks};
+use js::jsapi::{JSJitInfo, JSObject, JSTracer};
use js::jsapi::{
JS_DeprecatedStringHasLatin1Chars, JS_ResolveStandardClass, ObjectOpResult, StringIsArrayIndex,
};
@@ -44,7 +43,7 @@ use js::rust::wrappers::JS_GetPrototype;
use js::rust::wrappers::JS_HasProperty;
use js::rust::wrappers::JS_HasPropertyById;
use js::rust::wrappers::JS_SetProperty;
-use js::rust::{get_object_class, is_dom_class, GCMethods, ToString, ToWindowProxyIfWindow};
+use js::rust::{get_object_class, is_dom_class, GCMethods, ToString};
use js::rust::{Handle, HandleId, HandleObject, HandleValue, MutableHandleValue};
use js::typedarray::{CreateWith, Float32Array};
use js::JS_CALLEE;
@@ -53,6 +52,7 @@ use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::ptr;
use std::slice;
+use std::str;
/// Proxy handler for a WindowProxy.
pub struct WindowProxyHandler(pub *const libc::c_void);
@@ -475,36 +475,6 @@ pub unsafe extern "C" fn resolve_global(
true
}
-unsafe extern "C" fn wrap(
- cx: *mut JSContext,
- _existing: RawHandleObject,
- obj: RawHandleObject,
-) -> *mut JSObject {
- // FIXME terrible idea. need security wrappers
- // https://github.com/servo/servo/issues/2382
- WrapperNew(cx, obj, GetCrossCompartmentWrapper(), ptr::null(), false)
-}
-
-unsafe extern "C" fn pre_wrap(
- cx: *mut JSContext,
- _scope: RawHandleObject,
- _orig_obj: RawHandleObject,
- obj: RawHandleObject,
- _object_passed_to_wrap: RawHandleObject,
- rval: RawMutableHandleObject,
-) {
- let _ac = JSAutoRealm::new(cx, obj.get());
- let obj = ToWindowProxyIfWindow(obj.get());
- assert!(!obj.is_null());
- rval.set(obj)
-}
-
-/// Callback table for use with JS_SetWrapObjectCallbacks
-pub static WRAP_CALLBACKS: JSWrapObjectCallbacks = JSWrapObjectCallbacks {
- wrap: Some(wrap),
- preWrap: Some(pre_wrap),
-};
-
/// Deletes the property `id` from `object`.
pub unsafe fn delete_property_by_id(
cx: *mut JSContext,
diff --git a/components/script/dom/webidls/Location.webidl b/components/script/dom/webidls/Location.webidl
index 4120fc2731d..4d9fe2239f2 100644
--- a/components/script/dom/webidls/Location.webidl
+++ b/components/script/dom/webidls/Location.webidl
@@ -4,7 +4,8 @@
// https://html.spec.whatwg.org/multipage/#location
[Exposed=Window, Unforgeable] interface Location {
- [Throws] stringifier attribute USVString href;
+ [Throws, CrossOriginWritable]
+ stringifier attribute USVString href;
[Throws] readonly attribute USVString origin;
[Throws] attribute USVString protocol;
[Throws] attribute USVString host;
@@ -15,7 +16,8 @@
[Throws] attribute USVString hash;
[Throws] void assign(USVString url);
- [Throws] void replace(USVString url);
+ [Throws, CrossOriginCallable]
+ void replace(USVString url);
[Throws] void reload();
//[SameObject] readonly attribute USVString[] ancestorOrigins;
diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl
index fa3460e9889..289aa52cfb8 100644
--- a/components/script/dom/webidls/Window.webidl
+++ b/components/script/dom/webidls/Window.webidl
@@ -6,13 +6,14 @@
[Global=Window, Exposed=Window /*, LegacyUnenumerableNamedProperties */]
/*sealed*/ interface Window : GlobalScope {
// the current browsing context
- [Unforgeable] readonly attribute WindowProxy window;
- [BinaryName="Self_", Replaceable] readonly attribute WindowProxy self;
+ [Unforgeable, CrossOriginReadable] readonly attribute WindowProxy window;
+ [BinaryName="Self_", Replaceable, CrossOriginReadable] readonly attribute WindowProxy self;
[Unforgeable] readonly attribute Document document;
attribute DOMString name;
- [PutForwards=href, Unforgeable] readonly attribute Location location;
+ [PutForwards=href, Unforgeable, CrossOriginReadable, CrossOriginWritable]
+ readonly attribute Location location;
readonly attribute History history;
[Pref="dom.customelements.enabled"]
readonly attribute CustomElementRegistry customElements;
@@ -23,22 +24,22 @@
//[Replaceable] readonly attribute BarProp statusbar;
//[Replaceable] readonly attribute BarProp toolbar;
attribute DOMString status;
- void close();
- readonly attribute boolean closed;
+ [CrossOriginCallable] void close();
+ [CrossOriginReadable] readonly attribute boolean closed;
void stop();
- //void focus();
- //void blur();
+ //[CrossOriginCallable] void focus();
+ //[CrossOriginCallable] void blur();
// other browsing contexts
- [Replaceable] readonly attribute WindowProxy frames;
- [Replaceable] readonly attribute unsigned long length;
+ [Replaceable, CrossOriginReadable] readonly attribute WindowProxy frames;
+ [Replaceable, CrossOriginReadable] readonly attribute unsigned long length;
// Note that this can return null in the case that the browsing context has been discarded.
// https://github.com/whatwg/html/issues/2115
- [Unforgeable] readonly attribute WindowProxy? top;
- attribute any opener;
+ [Unforgeable, CrossOriginReadable] readonly attribute WindowProxy? top;
+ [CrossOriginReadable] attribute any opener;
// Note that this can return null in the case that the browsing context has been discarded.
// https://github.com/whatwg/html/issues/2115
- [Replaceable] readonly attribute WindowProxy? parent;
+ [Replaceable, CrossOriginReadable] readonly attribute WindowProxy? parent;
readonly attribute Element? frameElement;
[Throws] WindowProxy? open(optional USVString url = "", optional DOMString target = "_blank",
optional DOMString features = "");
@@ -63,9 +64,9 @@
unsigned long requestAnimationFrame(FrameRequestCallback callback);
void cancelAnimationFrame(unsigned long handle);
- [Throws]
+ [Throws, CrossOriginCallable]
void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer = []);
- [Throws]
+ [Throws, CrossOriginCallable]
void postMessage(any message, optional WindowPostMessageOptions options = {});
// also has obsolete members
diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs
index 244b42dc8a5..bccded0f43c 100644
--- a/components/script/dom/windowproxy.rs
+++ b/components/script/dom/windowproxy.rs
@@ -1084,6 +1084,9 @@ pub fn new_window_proxy_handler() -> WindowProxyHandler {
// These traps often throw security errors, and only pass on calls to methods
// defined in the DissimilarOriginWindow IDL.
+// TODO: reuse the infrastructure in `proxyhandler.rs`. For starters, the calls
+// to this function should be replaced with those to
+// `report_cross_origin_denial`.
#[allow(unsafe_code)]
unsafe fn throw_security_error(cx: *mut JSContext, realm: InRealm) -> bool {
if !JS_IsExceptionPending(cx) {
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index c44b4b04676..5ce21f01366 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -16,6 +16,7 @@ use crate::dom::bindings::conversions::private_from_object;
use crate::dom::bindings::conversions::root_from_handleobject;
use crate::dom::bindings::error::{throw_dom_exception, Error};
use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::principals;
use crate::dom::bindings::refcounted::{trace_refcounted_objects, LiveDOMReferences};
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::DomObject;
@@ -61,6 +62,7 @@ use js::jsapi::{
JSJitCompilerOption, JS_SetOffthreadIonCompilationEnabled, JS_SetParallelParsingEnabled,
};
use js::jsapi::{JSObject, PromiseRejectionHandlingState, SetPreserveWrapperCallbacks};
+use js::jsapi::{JSSecurityCallbacks, JS_InitDestroyPrincipalsCallback, JS_SetSecurityCallbacks};
use js::jsapi::{SetJobQueue, SetProcessBuildIdOp, SetPromiseRejectionTrackerCallback};
use js::jsval::UndefinedValue;
use js::panic::wrap_panic;
@@ -97,6 +99,12 @@ static JOB_QUEUE_TRAPS: JobQueueTraps = JobQueueTraps {
empty: Some(empty),
};
+static SECURITY_CALLBACKS: JSSecurityCallbacks = JSSecurityCallbacks {
+ // TODO: Content Security Policy <https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP>
+ contentSecurityPolicyAllows: None,
+ subsumes: Some(principals::subsumes),
+};
+
/// Common messages used to control the event loops in both the script and the worker
pub enum CommonScriptMsg {
/// Requests that the script thread measure its memory usage. The results are sent back via the
@@ -466,6 +474,10 @@ unsafe fn new_rt_and_cx_with_parent(
JS_AddExtraGCRootsTracer(cx, Some(trace_rust_roots), ptr::null_mut());
+ JS_SetSecurityCallbacks(cx, &SECURITY_CALLBACKS);
+
+ JS_InitDestroyPrincipalsCallback(cx, Some(principals::destroy_servo_jsprincipal));
+
// Needed for debug assertions about whether GC is running.
if cfg!(debug_assertions) {
JS_SetGCCallback(cx, Some(debug_gc_callback), ptr::null_mut());
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 8b3ba539809..c1161674fc2 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -35,7 +35,6 @@ use crate::dom::bindings::root::ThreadLocalStackRoots;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection};
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::JSTraceable;
-use crate::dom::bindings::utils::WRAP_CALLBACKS;
use crate::dom::customelementregistry::{
CallbackReaction, CustomElementDefinition, CustomElementReactionStack,
};
@@ -99,7 +98,6 @@ use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::GetWindowProxyClass;
-use js::jsapi::JS_SetWrapObjectCallbacks;
use js::jsapi::{
JSContext as UnsafeJSContext, JSTracer, JS_AddInterruptCallback, SetWindowProxyClass,
};
@@ -1295,7 +1293,6 @@ impl ScriptThread {
let cx = runtime.cx();
unsafe {
- JS_SetWrapObjectCallbacks(cx, &WRAP_CALLBACKS);
SetWindowProxyClass(cx, GetWindowProxyClass());
JS_AddInterruptCallback(cx, Some(interrupt_callback));
}