SimpleGlobalObject.cpp (5980B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/SimpleGlobalObject.h" 8 9 #include "js/Class.h" 10 #include "js/Object.h" // JS::GetClass, JS::GetObjectISupports, JS::SetObjectISupports 11 #include "jsapi.h" 12 #include "mozilla/NullPrincipal.h" 13 #include "mozilla/dom/ScriptSettings.h" 14 #include "nsContentUtils.h" 15 #include "nsJSPrincipals.h" 16 #include "nsThreadUtils.h" 17 #include "xpcprivate.h" 18 19 namespace mozilla::dom { 20 21 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(SimpleGlobalObject) 22 23 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SimpleGlobalObject) 24 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 25 tmp->UnlinkObjectsInGlobal(); 26 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 27 28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SimpleGlobalObject) 29 tmp->TraverseObjectsInGlobal(cb); 30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 31 32 NS_IMPL_CYCLE_COLLECTING_ADDREF(SimpleGlobalObject) 33 NS_IMPL_CYCLE_COLLECTING_RELEASE(SimpleGlobalObject) 34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SimpleGlobalObject) 35 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 36 NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) 37 NS_INTERFACE_MAP_END 38 39 static SimpleGlobalObject* GetSimpleGlobal(JSObject* global); 40 41 static void SimpleGlobal_finalize(JS::GCContext* gcx, JSObject* obj) { 42 SimpleGlobalObject* globalObject = GetSimpleGlobal(obj); 43 if (globalObject) { 44 globalObject->ClearWrapper(obj); 45 NS_RELEASE(globalObject); 46 } 47 } 48 49 static size_t SimpleGlobal_moved(JSObject* obj, JSObject* old) { 50 SimpleGlobalObject* globalObject = GetSimpleGlobal(obj); 51 if (globalObject) { 52 globalObject->UpdateWrapper(obj, old); 53 } 54 return 0; 55 } 56 57 static const JSClassOps SimpleGlobalClassOps = { 58 nullptr, 59 nullptr, 60 nullptr, 61 JS_NewEnumerateStandardClasses, 62 JS_ResolveStandardClass, 63 JS_MayResolveStandardClass, 64 SimpleGlobal_finalize, 65 nullptr, 66 nullptr, 67 JS_GlobalObjectTraceHook, 68 }; 69 70 static const js::ClassExtension SimpleGlobalClassExtension = { 71 SimpleGlobal_moved}; 72 73 static_assert(JSCLASS_GLOBAL_APPLICATION_SLOTS > 0, 74 "Need at least one slot for JSCLASS_SLOT0_IS_NSISUPPORTS"); 75 76 const JSClass SimpleGlobalClass = {"", 77 JSCLASS_GLOBAL_FLAGS | 78 JSCLASS_SLOT0_IS_NSISUPPORTS | 79 JSCLASS_FOREGROUND_FINALIZE, 80 &SimpleGlobalClassOps, 81 JS_NULL_CLASS_SPEC, 82 &SimpleGlobalClassExtension, 83 JS_NULL_OBJECT_OPS}; 84 85 static SimpleGlobalObject* GetSimpleGlobal(JSObject* global) { 86 MOZ_ASSERT(JS::GetClass(global) == &SimpleGlobalClass); 87 88 return JS::GetObjectISupports<SimpleGlobalObject>(global); 89 } 90 91 // static 92 JSObject* SimpleGlobalObject::Create(GlobalType globalType, 93 JS::Handle<JS::Value> proto) { 94 // We can't root our return value with our AutoJSAPI because the rooting 95 // analysis thinks ~AutoJSAPI can GC. So we need to root in a scope outside 96 // the lifetime of the AutoJSAPI. 97 JS::Rooted<JSObject*> global(RootingCx()); 98 99 { // Scope to ensure the AutoJSAPI destructor runs before we end up returning 100 AutoJSAPI jsapi; 101 jsapi.Init(); 102 JSContext* cx = jsapi.cx(); 103 104 JS::RealmOptions options; 105 options.creationOptions() 106 .setInvisibleToDebugger(true) 107 // Put our SimpleGlobalObjects in the system zone, so we won't create 108 // lots of zones for what are probably very short-lived 109 // compartments. This should help them be GCed quicker and take up 110 // less memory before they're GCed. 111 .setNewCompartmentInSystemZone(); 112 113 if (NS_IsMainThread()) { 114 nsCOMPtr<nsIPrincipal> principal = 115 NullPrincipal::CreateWithoutOriginAttributes(); 116 options.creationOptions().setTrace(xpc::TraceXPCGlobal); 117 global = xpc::CreateGlobalObject(cx, &SimpleGlobalClass, 118 nsJSPrincipals::get(principal), options); 119 } else { 120 global = JS_NewGlobalObject(cx, &SimpleGlobalClass, nullptr, 121 JS::DontFireOnNewGlobalHook, options); 122 } 123 124 if (!global) { 125 jsapi.ClearException(); 126 return nullptr; 127 } 128 129 JSAutoRealm ar(cx, global); 130 131 // It's important to create the nsIGlobalObject for our new global before we 132 // start trying to wrap things like the prototype into its compartment, 133 // because the wrap operation relies on the global having its 134 // nsIGlobalObject already. 135 RefPtr<SimpleGlobalObject> globalObject = 136 new SimpleGlobalObject(global, globalType); 137 138 JS::SetRealmReduceTimerPrecisionCallerType( 139 js::GetNonCCWObjectRealm(global), 140 RTPCallerTypeToToken(globalObject->GetRTPCallerType())); 141 142 // Pass on ownership of globalObject to |global|. 143 JS::SetObjectISupports(global, globalObject.forget().take()); 144 145 if (proto.isObjectOrNull()) { 146 JS::Rooted<JSObject*> protoObj(cx, proto.toObjectOrNull()); 147 if (!JS_WrapObject(cx, &protoObj)) { 148 jsapi.ClearException(); 149 return nullptr; 150 } 151 152 if (!JS_SetPrototype(cx, global, protoObj)) { 153 jsapi.ClearException(); 154 return nullptr; 155 } 156 } else if (!proto.isUndefined()) { 157 // Bogus proto. 158 return nullptr; 159 } 160 161 JS_FireOnNewGlobalObject(cx, global); 162 } 163 164 return global; 165 } 166 167 // static 168 SimpleGlobalObject::GlobalType SimpleGlobalObject::SimpleGlobalType( 169 JSObject* obj) { 170 if (JS::GetClass(obj) != &SimpleGlobalClass) { 171 return SimpleGlobalObject::GlobalType::NotSimpleGlobal; 172 } 173 174 SimpleGlobalObject* globalObject = GetSimpleGlobal(obj); 175 return globalObject->Type(); 176 } 177 178 } // namespace mozilla::dom