зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1702863 - Use a dedicate object for cross-realm weak map key. r=jandem,peterv
Differential Revision: https://phabricator.services.mozilla.com/D110954
This commit is contained in:
Родитель
ffa4291708
Коммит
55af083348
|
@ -269,13 +269,26 @@ bool MaybeCrossOriginObjectMixins::EnsureHolder(
|
|||
// our objects are per-Realm singletons, we are basically using "obj" itself
|
||||
// as part of the key.
|
||||
//
|
||||
// To represent the current settings, we use the current-Realm
|
||||
// Object.prototype. We can't use the current global, because we can't get a
|
||||
// useful cross-compartment wrapper for it; such wrappers would always go
|
||||
// To represent the current settings, we use a dedicated key object of the
|
||||
// current-Realm.
|
||||
//
|
||||
// We can't use the current global, because we can't get a useful
|
||||
// cross-compartment wrapper for it; such wrappers would always go
|
||||
// through a WindowProxy and would not be guarantee to keep pointing to a
|
||||
// single Realm when unwrapped. We want to grab this key before we start
|
||||
// changing Realms.
|
||||
JS::Rooted<JSObject*> key(cx, JS::GetRealmObjectPrototype(cx));
|
||||
//
|
||||
// Also we can't use arbitrary object (e.g.: Object.prototype), because at
|
||||
// this point those compartments are not same-origin, and don't have access to
|
||||
// each other, and the object retrieved here will be wrapped by a security
|
||||
// wrapper below, and the wrapper will be stored into the cache
|
||||
// (see Compartment::wrap). Those compartments can get access later by
|
||||
// modifying `document.domain`, and wrapping objects after that point
|
||||
// shouldn't result in a security wrapper. Wrap operation looks up the
|
||||
// existing wrapper in the cache, that contains the security wrapper created
|
||||
// here. We should use unique/private object here, so that this doesn't
|
||||
// affect later wrap operation.
|
||||
JS::Rooted<JSObject*> key(cx, JS::GetRealmWeakMapKey(cx));
|
||||
if (!key) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -702,7 +702,7 @@ static const uint32_t JSCLASS_FOREGROUND_FINALIZE =
|
|||
// application.
|
||||
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
|
||||
static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
|
||||
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 28;
|
||||
JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 29;
|
||||
|
||||
static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) {
|
||||
return JSCLASS_IS_GLOBAL |
|
||||
|
|
|
@ -102,6 +102,10 @@ extern JS_PUBLIC_API JSObject* GetRealmErrorPrototype(JSContext* cx);
|
|||
|
||||
extern JS_PUBLIC_API JSObject* GetRealmIteratorPrototype(JSContext* cx);
|
||||
|
||||
// Returns a key for cross-origin realm weak map.
|
||||
// See the consumer in `MaybeCrossOriginObjectMixins::EnsureHolder` for details.
|
||||
extern JS_PUBLIC_API JSObject* GetRealmWeakMapKey(JSContext* cx);
|
||||
|
||||
// Implements https://tc39.github.io/ecma262/#sec-getfunctionrealm
|
||||
// 7.3.22 GetFunctionRealm ( obj )
|
||||
//
|
||||
|
|
|
@ -963,6 +963,24 @@ NativeObject* GlobalObject::getOrCreateForOfPICObject(
|
|||
return forOfPIC;
|
||||
}
|
||||
|
||||
/* static */
|
||||
JSObject* GlobalObject::getOrCreateRealmWeakMapKey(
|
||||
JSContext* cx, Handle<GlobalObject*> global) {
|
||||
cx->check(global);
|
||||
Value v = global->getReservedSlot(REALM_WEAK_MAP_KEY);
|
||||
if (v.isObject()) {
|
||||
return &v.toObject();
|
||||
}
|
||||
|
||||
PlainObject* key = NewBuiltinClassInstance<PlainObject>(cx);
|
||||
if (!key) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
global->setReservedSlot(REALM_WEAK_MAP_KEY, ObjectValue(*key));
|
||||
return key;
|
||||
}
|
||||
|
||||
/* static */
|
||||
RegExpStatics* GlobalObject::getRegExpStatics(JSContext* cx,
|
||||
Handle<GlobalObject*> global) {
|
||||
|
|
|
@ -123,6 +123,7 @@ class GlobalObject : public NativeObject {
|
|||
GLOBAL_THIS_RESOLVED,
|
||||
INSTRUMENTATION,
|
||||
SOURCE_URLS,
|
||||
REALM_WEAK_MAP_KEY,
|
||||
|
||||
/* Total reserved-slot count for global objects. */
|
||||
RESERVED_SLOTS
|
||||
|
@ -894,6 +895,10 @@ class GlobalObject : public NativeObject {
|
|||
getSlotRef(SOURCE_URLS).unbarrieredSet(UndefinedValue());
|
||||
}
|
||||
|
||||
// Returns a key for a weak map, used by embedder.
|
||||
static JSObject* getOrCreateRealmWeakMapKey(JSContext* cx,
|
||||
Handle<GlobalObject*> global);
|
||||
|
||||
// A class used in place of a prototype during off-thread parsing.
|
||||
struct OffThreadPlaceholderObject : public NativeObject {
|
||||
static const int32_t SlotIndexSlot = 0;
|
||||
|
|
|
@ -786,6 +786,10 @@ JS_PUBLIC_API JSObject* JS::GetRealmIteratorPrototype(JSContext* cx) {
|
|||
return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
|
||||
}
|
||||
|
||||
JS_PUBLIC_API JSObject* JS::GetRealmWeakMapKey(JSContext* cx) {
|
||||
return GlobalObject::getOrCreateRealmWeakMapKey(cx, cx->global());
|
||||
}
|
||||
|
||||
JS_PUBLIC_API Realm* JS::GetFunctionRealm(JSContext* cx, HandleObject objArg) {
|
||||
// https://tc39.github.io/ecma262/#sec-getfunctionrealm
|
||||
// 7.3.22 GetFunctionRealm ( obj )
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
support-files =
|
||||
browser_consoleStack.html
|
||||
browser_deadObjectOnUnload.html
|
||||
browser_realm_weak_map_object_prototype_top.html
|
||||
browser_realm_weak_map_object_prototype_frame.html
|
||||
browser_realm_weak_map_promise_top.html
|
||||
browser_realm_weak_map_promise_frame.html
|
||||
[browser_dead_object.js]
|
||||
[browser_exception_leak.js]
|
||||
[browser_parent_process_hang_telemetry.js]
|
||||
[browser_realm_weak_map_and_document_domain.js]
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
async function test_document(url) {
|
||||
await BrowserTestUtils.withNewTab(url, async function (browser) {
|
||||
let result = await ContentTask.spawn(
|
||||
browser, {},
|
||||
async function() {
|
||||
let result = content.document.getElementById("result");
|
||||
return result.innerText;
|
||||
}
|
||||
);
|
||||
is(result, "OK", "test succeeds");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_explicit_object_prototype() {
|
||||
await test_document("http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_weak_map_object_prototype_top.html");
|
||||
});
|
||||
|
||||
add_task(async function test_implicit_object_prototype() {
|
||||
await test_document("http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_weak_map_promise_top.html");
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
<script type="text/javascript">
|
||||
// Access to the top-level window property before getting access.
|
||||
// This will create an entry in cross-origin realm weak map.
|
||||
try {
|
||||
window.top.Object;
|
||||
} catch (e) {}
|
||||
|
||||
document.domain = "mochi.test";
|
||||
|
||||
window.top.check();
|
||||
</script>
|
|
@ -0,0 +1,12 @@
|
|||
<script type="text/javascript">
|
||||
document.domain = "mochi.test";
|
||||
function check() {
|
||||
// Ensure frame's Object.prototype is accessible.
|
||||
if (document.getElementById("frame").contentWindow.Object.prototype.toString.call({}) == "[object Object]") {
|
||||
document.getElementById("result").textContent = "OK";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<iframe id="frame" src="http://test2.mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_weak_map_object_prototype_frame.html">
|
||||
</iframe>
|
||||
<span id="result"></span>
|
|
@ -0,0 +1,17 @@
|
|||
<script type="text/javascript">
|
||||
// Access to the top-level window property before getting access.
|
||||
// This will create an entry in cross-origin realm weak map.
|
||||
try {
|
||||
window.top.P;
|
||||
} catch (e) {}
|
||||
|
||||
document.domain = "mochi.test";
|
||||
|
||||
// Ensure that frame's Object.prototype is accessible from top-level frame
|
||||
// when getting incumbent global object inside Promise handling.
|
||||
window.top.P.then(v => {
|
||||
if (v == 10) {
|
||||
window.top.document.getElementById("result").textContent = "OK";
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,7 @@
|
|||
<script type="text/javascript">
|
||||
document.domain = "mochi.test";
|
||||
window.P = new Promise(r => r(10));
|
||||
</script>
|
||||
<iframe src="http://test2.mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_weak_map_promise_frame.html">
|
||||
</iframe>
|
||||
<span id="result"></span>
|
Загрузка…
Ссылка в новой задаче