mirror of
https://github.com/servo/servo.git
synced 2025-07-22 14:53:49 +01:00
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 ___
This commit is contained in:
commit
bd92fad81a
21 changed files with 1304 additions and 208 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3814,7 +3814,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "mozjs"
|
||||
version = "0.14.1"
|
||||
source = "git+https://github.com/servo/rust-mozjs#a8b688ad32a852172536443d77baa844f59a23fa"
|
||||
source = "git+https://github.com/servo/rust-mozjs#09edacd032fadc861b0cb3a70711a5c8a9bd7f8e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"lazy_static",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
));
|
||||
|
|
|
@ -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;
|
||||
|
|
139
components/script/dom/bindings/principals.rs
Normal file
139
components/script/dom/bindings/principals.rs
Normal file
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
[allow_prototype_cycle_through_location.sub.html]
|
||||
type: testharness
|
||||
[same-origin, same-window location cycle]
|
||||
expected: FAIL
|
||||
|
||||
[cross-origin location has null prototype]
|
||||
expected: FAIL
|
||||
|
||||
[same-origin, different-window location cycle]
|
||||
expected: FAIL
|
||||
|
||||
[cross-origin, but joined via document.domain, location cycle]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[location-prevent-extensions.html]
|
||||
[Object.preventExtensions throws a TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Reflect.preventExtensions returns false]
|
||||
expected: FAIL
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
[location-prototype-setting-cross-origin-domain.sub.html]
|
||||
type: testharness
|
||||
[Cross-origin via document.domain: the prototype is null]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin via document.domain: setting the prototype to an empty object via __proto__ should throw a "SecurityError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin via document.domain: setting the prototype to an empty object via Reflect.setPrototypeOf should return false]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin via document.domain: setting the prototype to its original value via __proto__ should throw a "SecurityError" since it ends up in CrossOriginGetOwnProperty]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin via document.domain: setting the prototype to an empty object via Object.setPrototypeOf should throw a TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin via document.domain: the prototype must still be its original value]
|
||||
expected: FAIL
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
[location-prototype-setting-cross-origin.sub.html]
|
||||
type: testharness
|
||||
[Cross-origin: the prototype is null]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin: setting the prototype to an empty object via __proto__ should throw a "SecurityError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin: setting the prototype to an empty object via Reflect.setPrototypeOf should return false]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin: the prototype must still be null]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin: setting the prototype to null via __proto__ should throw a "SecurityError" since it ends up in CrossOriginGetOwnProperty]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin: setting the prototype to an empty object via Object.setPrototypeOf should throw a TypeError]
|
||||
expected: FAIL
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
[location-prototype-setting-goes-cross-origin-domain.sub.html]
|
||||
type: testharness
|
||||
[Became cross-origin via document.domain: the prototype is now null]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: setting the prototype to an empty object via __proto__ should throw a "SecurityError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: the prototype must still be null]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: setting the prototype to null via __proto__ should throw a "SecurityError" since it ends up in CrossOriginGetOwnProperty]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: setting the prototype to an empty object via Reflect.setPrototypeOf should return false]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: setting the prototype to the original value from before going cross-origin via __proto__ should throw a "SecurityError" DOMException]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: setting the prototype to the original value from before going cross-origin via Reflect.setPrototypeOf should return false]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: setting the prototype to an empty object via Object.setPrototypeOf should throw a TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Became cross-origin via document.domain: setting the prototype to the original value from before going cross-origin via Object.setPrototypeOf should throw a TypeError]
|
||||
expected: FAIL
|
|
@ -1,10 +0,0 @@
|
|||
[location-prototype-setting-same-origin-domain.sub.html]
|
||||
type: testharness
|
||||
[Same-origin-domain: setting the prototype to an empty object via Reflect.setPrototypeOf should return false]
|
||||
expected: FAIL
|
||||
[Same-origin-domain: setting the prototype to an empty object via Object.setPrototypeOf should throw a TypeError]
|
||||
expected: FAIL
|
||||
[Same-origin-domain: setting the prototype to an empty object via __proto__ should throw a TypeError]
|
||||
expected: FAIL
|
||||
[Same-origin-domain: the prototype must still be its original value]
|
||||
expected: FAIL
|
|
@ -1,13 +0,0 @@
|
|||
[location-prototype-setting-same-origin.html]
|
||||
type: testharness
|
||||
[Same-origin: setting the prototype to an empty object via Reflect.setPrototypeOf should return false]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin: setting the prototype to an empty object via Object.setPrototypeOf should throw a TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin: setting the prototype to an empty object via __proto__ should throw a TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin: the prototype must still be its original value]
|
||||
expected: FAIL
|
|
@ -1,3 +1,223 @@
|
|||
[cross-origin-objects.html]
|
||||
type: testharness
|
||||
disabled: https://github.com/servo/servo/issues/10964
|
||||
[Basic sanity-checking (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Basic sanity-checking (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Basic sanity-checking (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Only certain properties are accessible cross-origin (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Only certain properties are accessible cross-origin (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Only certain properties are accessible cross-origin (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Only certain properties are usable as cross-origin this objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Only certain properties are usable as cross-origin this objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Only certain properties are usable as cross-origin this objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetPrototypeOf\]\] should return null (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetPrototypeOf\]\] should return null (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetPrototypeOf\]\] should return null (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[SetPrototypeOf\]\] should return false (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[SetPrototypeOf\]\] should return false (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[SetPrototypeOf\]\] should return false (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[PreventExtensions\]\] should return false cross-origin objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[PreventExtensions\]\] should return false cross-origin objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[PreventExtensions\]\] should return false cross-origin objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Property descriptors for cross-origin properties should be set up correctly (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Property descriptors for cross-origin properties should be set up correctly (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Property descriptors for cross-origin properties should be set up correctly (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Subframe named 'then' should shadow the default 'then' value (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Subframe named 'then' should shadow the default 'then' value (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Subframe named 'then' should shadow the default 'then' value (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Subframes should be visible cross-origin only if their names don't match the names of cross-origin-exposed IDL properties (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Subframes should be visible cross-origin only if their names don't match the names of cross-origin-exposed IDL properties (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Subframes should be visible cross-origin only if their names don't match the names of cross-origin-exposed IDL properties (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Should be able to get a property descriptor for an indexed property only if it corresponds to a child window. (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Should be able to get a property descriptor for an indexed property only if it corresponds to a child window. (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[GetOwnProperty\]\] - Should be able to get a property descriptor for an indexed property only if it corresponds to a child window. (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[Delete\]\] Should throw on cross-origin objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[Delete\]\] Should throw on cross-origin objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[Delete\]\] Should throw on cross-origin objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[DefineOwnProperty\]\] Should throw for cross-origin objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[DefineOwnProperty\]\] Should throw for cross-origin objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[DefineOwnProperty\]\] Should throw for cross-origin objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Can only enumerate safelisted enumerable properties (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Can only enumerate safelisted enumerable properties (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Can only enumerate safelisted enumerable properties (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should return all properties from cross-origin objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should return all properties from cross-origin objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should return all properties from cross-origin objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should return the right symbol-named properties for cross-origin objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should return the right symbol-named properties for cross-origin objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should return the right symbol-named properties for cross-origin objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should place the symbols after the property names after the subframe indices (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should place the symbols after the property names after the subframe indices (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should place the symbols after the property names after the subframe indices (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should not reorder where 'then' appears if it's a named subframe, nor add another copy of 'then' (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should not reorder where 'then' appears if it's a named subframe, nor add another copy of 'then' (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[[[OwnPropertyKeys\]\] should not reorder where 'then' appears if it's a named subframe, nor add another copy of 'then' (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin functions get local Function.prototype (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin functions get local Function.prototype (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin functions get local Function.prototype (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin Window accessors get local Function.prototype (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin Window accessors get local Function.prototype (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin Window accessors get local Function.prototype (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different functions for cross-origin objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different functions for cross-origin objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different functions for cross-origin objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different accessors for cross-origin Window (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different accessors for cross-origin Window (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different accessors for cross-origin Window (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different accessors for cross-origin Location (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different accessors for cross-origin Location (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[Same-origin observers get different accessors for cross-origin Location (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[{}.toString.call() does the right thing on cross-origin objects (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[{}.toString.call() does the right thing on cross-origin objects (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[{}.toString.call() does the right thing on cross-origin objects (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Resolving a promise with a cross-origin window without a 'then' subframe should work (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[Resolving a promise with a cross-origin window with a 'then' subframe should work (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
[LegacyLenientThis behavior (cross-origin)]
|
||||
expected: FAIL
|
||||
|
||||
[LegacyLenientThis behavior (same-origin + document.domain)]
|
||||
expected: FAIL
|
||||
|
||||
[LegacyLenientThis behavior (cross-site)]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue