зеркало из 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
|
// our objects are per-Realm singletons, we are basically using "obj" itself
|
||||||
// as part of the key.
|
// as part of the key.
|
||||||
//
|
//
|
||||||
// To represent the current settings, we use the current-Realm
|
// To represent the current settings, we use a dedicated key object of the
|
||||||
// Object.prototype. We can't use the current global, because we can't get a
|
// current-Realm.
|
||||||
// useful cross-compartment wrapper for it; such wrappers would always go
|
//
|
||||||
|
// 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
|
// 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
|
// single Realm when unwrapped. We want to grab this key before we start
|
||||||
// changing Realms.
|
// 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) {
|
if (!key) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -702,7 +702,7 @@ static const uint32_t JSCLASS_FOREGROUND_FINALIZE =
|
||||||
// application.
|
// application.
|
||||||
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
|
static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5;
|
||||||
static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT =
|
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) {
|
static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) {
|
||||||
return JSCLASS_IS_GLOBAL |
|
return JSCLASS_IS_GLOBAL |
|
||||||
|
|
|
@ -102,6 +102,10 @@ extern JS_PUBLIC_API JSObject* GetRealmErrorPrototype(JSContext* cx);
|
||||||
|
|
||||||
extern JS_PUBLIC_API JSObject* GetRealmIteratorPrototype(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
|
// Implements https://tc39.github.io/ecma262/#sec-getfunctionrealm
|
||||||
// 7.3.22 GetFunctionRealm ( obj )
|
// 7.3.22 GetFunctionRealm ( obj )
|
||||||
//
|
//
|
||||||
|
|
|
@ -963,6 +963,24 @@ NativeObject* GlobalObject::getOrCreateForOfPICObject(
|
||||||
return forOfPIC;
|
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 */
|
/* static */
|
||||||
RegExpStatics* GlobalObject::getRegExpStatics(JSContext* cx,
|
RegExpStatics* GlobalObject::getRegExpStatics(JSContext* cx,
|
||||||
Handle<GlobalObject*> global) {
|
Handle<GlobalObject*> global) {
|
||||||
|
|
|
@ -123,6 +123,7 @@ class GlobalObject : public NativeObject {
|
||||||
GLOBAL_THIS_RESOLVED,
|
GLOBAL_THIS_RESOLVED,
|
||||||
INSTRUMENTATION,
|
INSTRUMENTATION,
|
||||||
SOURCE_URLS,
|
SOURCE_URLS,
|
||||||
|
REALM_WEAK_MAP_KEY,
|
||||||
|
|
||||||
/* Total reserved-slot count for global objects. */
|
/* Total reserved-slot count for global objects. */
|
||||||
RESERVED_SLOTS
|
RESERVED_SLOTS
|
||||||
|
@ -894,6 +895,10 @@ class GlobalObject : public NativeObject {
|
||||||
getSlotRef(SOURCE_URLS).unbarrieredSet(UndefinedValue());
|
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.
|
// A class used in place of a prototype during off-thread parsing.
|
||||||
struct OffThreadPlaceholderObject : public NativeObject {
|
struct OffThreadPlaceholderObject : public NativeObject {
|
||||||
static const int32_t SlotIndexSlot = 0;
|
static const int32_t SlotIndexSlot = 0;
|
||||||
|
|
|
@ -786,6 +786,10 @@ JS_PUBLIC_API JSObject* JS::GetRealmIteratorPrototype(JSContext* cx) {
|
||||||
return GlobalObject::getOrCreateIteratorPrototype(cx, cx->global());
|
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) {
|
JS_PUBLIC_API Realm* JS::GetFunctionRealm(JSContext* cx, HandleObject objArg) {
|
||||||
// https://tc39.github.io/ecma262/#sec-getfunctionrealm
|
// https://tc39.github.io/ecma262/#sec-getfunctionrealm
|
||||||
// 7.3.22 GetFunctionRealm ( obj )
|
// 7.3.22 GetFunctionRealm ( obj )
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
support-files =
|
support-files =
|
||||||
browser_consoleStack.html
|
browser_consoleStack.html
|
||||||
browser_deadObjectOnUnload.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_dead_object.js]
|
||||||
[browser_exception_leak.js]
|
[browser_exception_leak.js]
|
||||||
[browser_parent_process_hang_telemetry.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>
|
Загрузка…
Ссылка в новой задаче