js/xpconnect/src/XPCWrappedNativeScope.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* Class used to manage the wrapped native objects within a JS scope. */
michael@0 8
michael@0 9 #include "xpcprivate.h"
michael@0 10 #include "XPCWrapper.h"
michael@0 11 #include "nsContentUtils.h"
michael@0 12 #include "nsCycleCollectionNoteRootCallback.h"
michael@0 13 #include "nsPrincipal.h"
michael@0 14 #include "mozilla/MemoryReporting.h"
michael@0 15 #include "mozilla/Preferences.h"
michael@0 16
michael@0 17 #include "mozilla/dom/BindingUtils.h"
michael@0 18
michael@0 19 using namespace mozilla;
michael@0 20 using namespace xpc;
michael@0 21 using namespace JS;
michael@0 22
michael@0 23 /***************************************************************************/
michael@0 24
michael@0 25 XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nullptr;
michael@0 26 XPCWrappedNativeScope* XPCWrappedNativeScope::gDyingScopes = nullptr;
michael@0 27
michael@0 28 // static
michael@0 29 XPCWrappedNativeScope*
michael@0 30 XPCWrappedNativeScope::GetNewOrUsed(JSContext *cx, JS::HandleObject aGlobal)
michael@0 31 {
michael@0 32 XPCWrappedNativeScope* scope = GetObjectScope(aGlobal);
michael@0 33 if (!scope) {
michael@0 34 scope = new XPCWrappedNativeScope(cx, aGlobal);
michael@0 35 }
michael@0 36 return scope;
michael@0 37 }
michael@0 38
michael@0 39 static bool
michael@0 40 RemoteXULForbidsXBLScope(nsIPrincipal *aPrincipal, HandleObject aGlobal)
michael@0 41 {
michael@0 42 // Check for random JSD scopes that don't have a principal.
michael@0 43 if (!aPrincipal)
michael@0 44 return false;
michael@0 45
michael@0 46 // The SafeJSContext is lazily created, and tends to be created at really
michael@0 47 // weird times, at least for xpcshell (often very early in startup or late
michael@0 48 // in shutdown). Its scope isn't system principal, so if we proceeded we'd
michael@0 49 // end up calling into AllowXULXBLForPrincipal, which depends on all kinds
michael@0 50 // of persistent storage and permission machinery that may or not be running.
michael@0 51 // We know the answer to the question here, so just short-circuit.
michael@0 52 if (JS_GetClass(aGlobal) == &SafeJSContextGlobalClass)
michael@0 53 return false;
michael@0 54
michael@0 55 // AllowXULXBLForPrincipal will return true for system principal, but we
michael@0 56 // don't want that here.
michael@0 57 MOZ_ASSERT(nsContentUtils::IsInitialized());
michael@0 58 if (nsContentUtils::IsSystemPrincipal(aPrincipal))
michael@0 59 return false;
michael@0 60
michael@0 61 // If this domain isn't whitelisted, we're done.
michael@0 62 if (!nsContentUtils::AllowXULXBLForPrincipal(aPrincipal))
michael@0 63 return false;
michael@0 64
michael@0 65 // Check the pref to determine how we should behave.
michael@0 66 return !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false);
michael@0 67 }
michael@0 68
michael@0 69 XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx,
michael@0 70 JS::HandleObject aGlobal)
michael@0 71 : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)),
michael@0 72 mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
michael@0 73 mComponents(nullptr),
michael@0 74 mNext(nullptr),
michael@0 75 mGlobalJSObject(aGlobal),
michael@0 76 mIsXBLScope(false)
michael@0 77 {
michael@0 78 // add ourselves to the scopes list
michael@0 79 {
michael@0 80 MOZ_ASSERT(aGlobal);
michael@0 81 MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
michael@0 82 JSCLASS_HAS_PRIVATE));
michael@0 83 #ifdef DEBUG
michael@0 84 for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
michael@0 85 MOZ_ASSERT(aGlobal != cur->GetGlobalJSObjectPreserveColor(), "dup object");
michael@0 86 #endif
michael@0 87
michael@0 88 mNext = gScopes;
michael@0 89 gScopes = this;
michael@0 90
michael@0 91 // Grab the XPCContext associated with our context.
michael@0 92 mContext = XPCContext::GetXPCContext(cx);
michael@0 93 mContext->AddScope(this);
michael@0 94 }
michael@0 95
michael@0 96 MOZ_COUNT_CTOR(XPCWrappedNativeScope);
michael@0 97
michael@0 98 // Attach ourselves to the compartment private.
michael@0 99 CompartmentPrivate *priv = EnsureCompartmentPrivate(aGlobal);
michael@0 100 priv->scope = this;
michael@0 101
michael@0 102 // Determine whether we would allow an XBL scope in this situation.
michael@0 103 // In addition to being pref-controlled, we also disable XBL scopes for
michael@0 104 // remote XUL domains, _except_ if we have an additional pref override set.
michael@0 105 nsIPrincipal *principal = GetPrincipal();
michael@0 106 mAllowXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal);
michael@0 107
michael@0 108 // Determine whether to use an XBL scope.
michael@0 109 mUseXBLScope = mAllowXBLScope;
michael@0 110 if (mUseXBLScope) {
michael@0 111 const js::Class *clasp = js::GetObjectClass(mGlobalJSObject);
michael@0 112 mUseXBLScope = !strcmp(clasp->name, "Window") ||
michael@0 113 !strcmp(clasp->name, "ChromeWindow") ||
michael@0 114 !strcmp(clasp->name, "ModalContentWindow");
michael@0 115 }
michael@0 116 if (mUseXBLScope) {
michael@0 117 mUseXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
michael@0 118 }
michael@0 119 }
michael@0 120
michael@0 121 // static
michael@0 122 bool
michael@0 123 XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope *scope)
michael@0 124 {
michael@0 125 for (XPCWrappedNativeScope *cur = gDyingScopes; cur; cur = cur->mNext) {
michael@0 126 if (scope == cur)
michael@0 127 return true;
michael@0 128 }
michael@0 129 return false;
michael@0 130 }
michael@0 131
michael@0 132 bool
michael@0 133 XPCWrappedNativeScope::GetComponentsJSObject(JS::MutableHandleObject obj)
michael@0 134 {
michael@0 135 AutoJSContext cx;
michael@0 136 if (!mComponents) {
michael@0 137 nsIPrincipal *p = GetPrincipal();
michael@0 138 bool system = XPCWrapper::GetSecurityManager()->IsSystemPrincipal(p);
michael@0 139 mComponents = system ? new nsXPCComponents(this)
michael@0 140 : new nsXPCComponentsBase(this);
michael@0 141 }
michael@0 142
michael@0 143 RootedValue val(cx);
michael@0 144 xpcObjectHelper helper(mComponents);
michael@0 145 bool ok = XPCConvert::NativeInterface2JSObject(&val, nullptr, helper,
michael@0 146 nullptr, nullptr, false,
michael@0 147 nullptr);
michael@0 148 if (NS_WARN_IF(!ok))
michael@0 149 return false;
michael@0 150
michael@0 151 if (NS_WARN_IF(!val.isObject()))
michael@0 152 return false;
michael@0 153
michael@0 154 // The call to wrap() here is necessary even though the object is same-
michael@0 155 // compartment, because it applies our security wrapper.
michael@0 156 obj.set(&val.toObject());
michael@0 157 if (NS_WARN_IF(!JS_WrapObject(cx, obj)))
michael@0 158 return false;
michael@0 159 return true;
michael@0 160 }
michael@0 161
michael@0 162 void
michael@0 163 XPCWrappedNativeScope::ForcePrivilegedComponents()
michael@0 164 {
michael@0 165 // This may only be called on unprivileged scopes during automation where
michael@0 166 // we allow insecure things.
michael@0 167 MOZ_RELEASE_ASSERT(Preferences::GetBool("security.turn_off_all_security_so_"
michael@0 168 "that_viruses_can_take_over_this_"
michael@0 169 "computer"));
michael@0 170 nsCOMPtr<nsIXPCComponents> c = do_QueryInterface(mComponents);
michael@0 171 if (!c)
michael@0 172 mComponents = new nsXPCComponents(this);
michael@0 173 }
michael@0 174
michael@0 175 bool
michael@0 176 XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx)
michael@0 177 {
michael@0 178 RootedObject components(aCx);
michael@0 179 if (!GetComponentsJSObject(&components))
michael@0 180 return false;
michael@0 181
michael@0 182 RootedObject global(aCx, GetGlobalJSObject());
michael@0 183 MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx));
michael@0 184
michael@0 185 RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS));
michael@0 186 return JS_DefinePropertyById(aCx, global, id, ObjectValue(*components),
michael@0 187 nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY);
michael@0 188 }
michael@0 189
michael@0 190 JSObject*
michael@0 191 XPCWrappedNativeScope::EnsureXBLScope(JSContext *cx)
michael@0 192 {
michael@0 193 JS::RootedObject global(cx, GetGlobalJSObject());
michael@0 194 MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
michael@0 195 MOZ_ASSERT(!mIsXBLScope);
michael@0 196 MOZ_ASSERT(strcmp(js::GetObjectClass(global)->name,
michael@0 197 "nsXBLPrototypeScript compilation scope"));
michael@0 198
michael@0 199 // If we already have a special XBL scope object, we know what to use.
michael@0 200 if (mXBLScope)
michael@0 201 return mXBLScope;
michael@0 202
michael@0 203 // If this scope doesn't need an XBL scope, just return the global.
michael@0 204 if (!mUseXBLScope)
michael@0 205 return global;
michael@0 206
michael@0 207 // Set up the sandbox options. Note that we use the DOM global as the
michael@0 208 // sandboxPrototype so that the XBL scope can access all the DOM objects
michael@0 209 // it's accustomed to accessing.
michael@0 210 //
michael@0 211 // NB: One would think that wantXrays wouldn't make a difference here.
michael@0 212 // However, wantXrays lives a secret double life, and one of its other
michael@0 213 // hobbies is to waive Xray on the returned sandbox when set to false.
michael@0 214 // So make sure to keep this set to true, here.
michael@0 215 SandboxOptions options;
michael@0 216 options.wantXrays = true;
michael@0 217 options.wantComponents = true;
michael@0 218 options.proto = global;
michael@0 219 options.sameZoneAs = global;
michael@0 220
michael@0 221 // Use an nsExpandedPrincipal to create asymmetric security.
michael@0 222 nsIPrincipal *principal = GetPrincipal();
michael@0 223 nsCOMPtr<nsIExpandedPrincipal> ep;
michael@0 224 MOZ_ASSERT(!(ep = do_QueryInterface(principal)));
michael@0 225 nsTArray< nsCOMPtr<nsIPrincipal> > principalAsArray(1);
michael@0 226 principalAsArray.AppendElement(principal);
michael@0 227 ep = new nsExpandedPrincipal(principalAsArray);
michael@0 228
michael@0 229 // Create the sandbox.
michael@0 230 RootedValue v(cx);
michael@0 231 nsresult rv = CreateSandboxObject(cx, &v, ep, options);
michael@0 232 NS_ENSURE_SUCCESS(rv, nullptr);
michael@0 233 mXBLScope = &v.toObject();
michael@0 234
michael@0 235 // Tag it.
michael@0 236 EnsureCompartmentPrivate(js::UncheckedUnwrap(mXBLScope))->scope->mIsXBLScope = true;
michael@0 237
michael@0 238 // Good to go!
michael@0 239 return mXBLScope;
michael@0 240 }
michael@0 241
michael@0 242 bool
michael@0 243 XPCWrappedNativeScope::AllowXBLScope()
michael@0 244 {
michael@0 245 // We only disallow XBL scopes in remote XUL situations.
michael@0 246 MOZ_ASSERT_IF(!mAllowXBLScope,
michael@0 247 nsContentUtils::AllowXULXBLForPrincipal(GetPrincipal()));
michael@0 248 return mAllowXBLScope;
michael@0 249 }
michael@0 250
michael@0 251 namespace xpc {
michael@0 252 JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
michael@0 253 {
michael@0 254 JS::RootedObject contentScope(cx, contentScopeArg);
michael@0 255 JSAutoCompartment ac(cx, contentScope);
michael@0 256 JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureXBLScope(cx);
michael@0 257 NS_ENSURE_TRUE(scope, nullptr); // See bug 858642.
michael@0 258 scope = js::UncheckedUnwrap(scope);
michael@0 259 JS::ExposeObjectToActiveJS(scope);
michael@0 260 return scope;
michael@0 261 }
michael@0 262
michael@0 263 bool AllowXBLScope(JSCompartment *c)
michael@0 264 {
michael@0 265 XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
michael@0 266 return scope && scope->AllowXBLScope();
michael@0 267 }
michael@0 268
michael@0 269 bool UseXBLScope(JSCompartment *c)
michael@0 270 {
michael@0 271 XPCWrappedNativeScope *scope = EnsureCompartmentPrivate(c)->scope;
michael@0 272 return scope && scope->UseXBLScope();
michael@0 273 }
michael@0 274
michael@0 275 } /* namespace xpc */
michael@0 276
michael@0 277 XPCWrappedNativeScope::~XPCWrappedNativeScope()
michael@0 278 {
michael@0 279 MOZ_COUNT_DTOR(XPCWrappedNativeScope);
michael@0 280
michael@0 281 // We can do additional cleanup assertions here...
michael@0 282
michael@0 283 if (mWrappedNativeMap) {
michael@0 284 MOZ_ASSERT(0 == mWrappedNativeMap->Count(), "scope has non-empty map");
michael@0 285 delete mWrappedNativeMap;
michael@0 286 }
michael@0 287
michael@0 288 if (mWrappedNativeProtoMap) {
michael@0 289 MOZ_ASSERT(0 == mWrappedNativeProtoMap->Count(), "scope has non-empty map");
michael@0 290 delete mWrappedNativeProtoMap;
michael@0 291 }
michael@0 292
michael@0 293 if (mContext)
michael@0 294 mContext->RemoveScope(this);
michael@0 295
michael@0 296 // This should not be necessary, since the Components object should die
michael@0 297 // with the scope but just in case.
michael@0 298 if (mComponents)
michael@0 299 mComponents->mScope = nullptr;
michael@0 300
michael@0 301 // XXX we should assert that we are dead or that xpconnect has shutdown
michael@0 302 // XXX might not want to do this at xpconnect shutdown time???
michael@0 303 mComponents = nullptr;
michael@0 304
michael@0 305 if (mXrayExpandos.initialized())
michael@0 306 mXrayExpandos.destroy();
michael@0 307
michael@0 308 JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
michael@0 309 mXBLScope.finalize(rt);
michael@0 310 mGlobalJSObject.finalize(rt);
michael@0 311 }
michael@0 312
michael@0 313 static PLDHashOperator
michael@0 314 WrappedNativeJSGCThingTracer(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 315 uint32_t number, void *arg)
michael@0 316 {
michael@0 317 XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
michael@0 318 if (wrapper->HasExternalReference() && !wrapper->IsWrapperExpired())
michael@0 319 wrapper->TraceSelf((JSTracer *)arg);
michael@0 320
michael@0 321 return PL_DHASH_NEXT;
michael@0 322 }
michael@0 323
michael@0 324 // static
michael@0 325 void
michael@0 326 XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt)
michael@0 327 {
michael@0 328 // Do JS_CallTracer for all wrapped natives with external references, as
michael@0 329 // well as any DOM expando objects.
michael@0 330 for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
michael@0 331 cur->mWrappedNativeMap->Enumerate(WrappedNativeJSGCThingTracer, trc);
michael@0 332 if (cur->mDOMExpandoSet) {
michael@0 333 for (DOMExpandoSet::Enum e(*cur->mDOMExpandoSet); !e.empty(); e.popFront())
michael@0 334 JS_CallHashSetObjectTracer(trc, e, e.front(), "DOM expando object");
michael@0 335 }
michael@0 336 }
michael@0 337 }
michael@0 338
michael@0 339 static PLDHashOperator
michael@0 340 WrappedNativeSuspecter(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 341 uint32_t number, void *arg)
michael@0 342 {
michael@0 343 XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
michael@0 344
michael@0 345 if (wrapper->HasExternalReference()) {
michael@0 346 nsCycleCollectionNoteRootCallback *cb =
michael@0 347 static_cast<nsCycleCollectionNoteRootCallback *>(arg);
michael@0 348 XPCJSRuntime::SuspectWrappedNative(wrapper, *cb);
michael@0 349 }
michael@0 350
michael@0 351 return PL_DHASH_NEXT;
michael@0 352 }
michael@0 353
michael@0 354 static void
michael@0 355 SuspectDOMExpandos(JSObject *obj, nsCycleCollectionNoteRootCallback &cb)
michael@0 356 {
michael@0 357 MOZ_ASSERT(dom::GetDOMClass(obj) && dom::GetDOMClass(obj)->mDOMObjectIsISupports);
michael@0 358 nsISupports* native = dom::UnwrapDOMObject<nsISupports>(obj);
michael@0 359 cb.NoteXPCOMRoot(native);
michael@0 360 }
michael@0 361
michael@0 362 // static
michael@0 363 void
michael@0 364 XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt,
michael@0 365 nsCycleCollectionNoteRootCallback& cb)
michael@0 366 {
michael@0 367 for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
michael@0 368 cur->mWrappedNativeMap->Enumerate(WrappedNativeSuspecter, &cb);
michael@0 369 if (cur->mDOMExpandoSet) {
michael@0 370 for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront())
michael@0 371 SuspectDOMExpandos(r.front(), cb);
michael@0 372 }
michael@0 373 }
michael@0 374 }
michael@0 375
michael@0 376 // static
michael@0 377 void
michael@0 378 XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt)
michael@0 379 {
michael@0 380 // We are in JSGC_MARK_END and JSGC_FINALIZE_END must always follow it
michael@0 381 // calling FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
michael@0 382 // KillDyingScopes.
michael@0 383 MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END");
michael@0 384
michael@0 385 XPCWrappedNativeScope* prev = nullptr;
michael@0 386 XPCWrappedNativeScope* cur = gScopes;
michael@0 387
michael@0 388 while (cur) {
michael@0 389 // Sweep waivers.
michael@0 390 if (cur->mWaiverWrapperMap)
michael@0 391 cur->mWaiverWrapperMap->Sweep();
michael@0 392
michael@0 393 XPCWrappedNativeScope* next = cur->mNext;
michael@0 394
michael@0 395 if (cur->mGlobalJSObject && cur->mGlobalJSObject.isAboutToBeFinalized()) {
michael@0 396 cur->mGlobalJSObject.finalize(fop->runtime());
michael@0 397 // Move this scope from the live list to the dying list.
michael@0 398 if (prev)
michael@0 399 prev->mNext = next;
michael@0 400 else
michael@0 401 gScopes = next;
michael@0 402 cur->mNext = gDyingScopes;
michael@0 403 gDyingScopes = cur;
michael@0 404 cur = nullptr;
michael@0 405 }
michael@0 406 if (cur)
michael@0 407 prev = cur;
michael@0 408 cur = next;
michael@0 409 }
michael@0 410 }
michael@0 411
michael@0 412 // static
michael@0 413 void
michael@0 414 XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC()
michael@0 415 {
michael@0 416 KillDyingScopes();
michael@0 417 }
michael@0 418
michael@0 419 static PLDHashOperator
michael@0 420 WrappedNativeMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 421 uint32_t number_t, void *arg)
michael@0 422 {
michael@0 423 ((Native2WrappedNativeMap::Entry*)hdr)->value->Mark();
michael@0 424 return PL_DHASH_NEXT;
michael@0 425 }
michael@0 426
michael@0 427 // We need to explicitly mark all the protos too because some protos may be
michael@0 428 // alive in the hashtable but not currently in use by any wrapper
michael@0 429 static PLDHashOperator
michael@0 430 WrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 431 uint32_t number, void *arg)
michael@0 432 {
michael@0 433 ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->Mark();
michael@0 434 return PL_DHASH_NEXT;
michael@0 435 }
michael@0 436
michael@0 437 // static
michael@0 438 void
michael@0 439 XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos()
michael@0 440 {
michael@0 441 for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
michael@0 442 cur->mWrappedNativeMap->Enumerate(WrappedNativeMarker, nullptr);
michael@0 443 cur->mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMarker, nullptr);
michael@0 444 }
michael@0 445 }
michael@0 446
michael@0 447 #ifdef DEBUG
michael@0 448 static PLDHashOperator
michael@0 449 ASSERT_WrappedNativeSetNotMarked(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 450 uint32_t number, void *arg)
michael@0 451 {
michael@0 452 ((Native2WrappedNativeMap::Entry*)hdr)->value->ASSERT_SetsNotMarked();
michael@0 453 return PL_DHASH_NEXT;
michael@0 454 }
michael@0 455
michael@0 456 static PLDHashOperator
michael@0 457 ASSERT_WrappedNativeProtoSetNotMarked(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 458 uint32_t number, void *arg)
michael@0 459 {
michael@0 460 ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->ASSERT_SetNotMarked();
michael@0 461 return PL_DHASH_NEXT;
michael@0 462 }
michael@0 463
michael@0 464 // static
michael@0 465 void
michael@0 466 XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked()
michael@0 467 {
michael@0 468 for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
michael@0 469 cur->mWrappedNativeMap->Enumerate(ASSERT_WrappedNativeSetNotMarked, nullptr);
michael@0 470 cur->mWrappedNativeProtoMap->Enumerate(ASSERT_WrappedNativeProtoSetNotMarked, nullptr);
michael@0 471 }
michael@0 472 }
michael@0 473 #endif
michael@0 474
michael@0 475 static PLDHashOperator
michael@0 476 WrappedNativeTearoffSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 477 uint32_t number, void *arg)
michael@0 478 {
michael@0 479 ((Native2WrappedNativeMap::Entry*)hdr)->value->SweepTearOffs();
michael@0 480 return PL_DHASH_NEXT;
michael@0 481 }
michael@0 482
michael@0 483 // static
michael@0 484 void
michael@0 485 XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs()
michael@0 486 {
michael@0 487 for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
michael@0 488 cur->mWrappedNativeMap->Enumerate(WrappedNativeTearoffSweeper, nullptr);
michael@0 489 }
michael@0 490
michael@0 491 // static
michael@0 492 void
michael@0 493 XPCWrappedNativeScope::KillDyingScopes()
michael@0 494 {
michael@0 495 XPCWrappedNativeScope* cur = gDyingScopes;
michael@0 496 while (cur) {
michael@0 497 XPCWrappedNativeScope* next = cur->mNext;
michael@0 498 if (cur->mGlobalJSObject)
michael@0 499 GetCompartmentPrivate(cur->mGlobalJSObject)->scope = nullptr;
michael@0 500 delete cur;
michael@0 501 cur = next;
michael@0 502 }
michael@0 503 gDyingScopes = nullptr;
michael@0 504 }
michael@0 505
michael@0 506 struct ShutdownData
michael@0 507 {
michael@0 508 ShutdownData()
michael@0 509 : wrapperCount(0),
michael@0 510 protoCount(0) {}
michael@0 511 int wrapperCount;
michael@0 512 int protoCount;
michael@0 513 };
michael@0 514
michael@0 515 static PLDHashOperator
michael@0 516 WrappedNativeShutdownEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 517 uint32_t number, void *arg)
michael@0 518 {
michael@0 519 ShutdownData* data = (ShutdownData*) arg;
michael@0 520 XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
michael@0 521
michael@0 522 if (wrapper->IsValid()) {
michael@0 523 wrapper->SystemIsBeingShutDown();
michael@0 524 data->wrapperCount++;
michael@0 525 }
michael@0 526 return PL_DHASH_REMOVE;
michael@0 527 }
michael@0 528
michael@0 529 static PLDHashOperator
michael@0 530 WrappedNativeProtoShutdownEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 531 uint32_t number, void *arg)
michael@0 532 {
michael@0 533 ShutdownData* data = (ShutdownData*) arg;
michael@0 534 ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->
michael@0 535 SystemIsBeingShutDown();
michael@0 536 data->protoCount++;
michael@0 537 return PL_DHASH_REMOVE;
michael@0 538 }
michael@0 539
michael@0 540 //static
michael@0 541 void
michael@0 542 XPCWrappedNativeScope::SystemIsBeingShutDown()
michael@0 543 {
michael@0 544 int liveScopeCount = 0;
michael@0 545
michael@0 546 ShutdownData data;
michael@0 547
michael@0 548 XPCWrappedNativeScope* cur;
michael@0 549
michael@0 550 // First move all the scopes to the dying list.
michael@0 551
michael@0 552 cur = gScopes;
michael@0 553 while (cur) {
michael@0 554 XPCWrappedNativeScope* next = cur->mNext;
michael@0 555 cur->mNext = gDyingScopes;
michael@0 556 gDyingScopes = cur;
michael@0 557 cur = next;
michael@0 558 liveScopeCount++;
michael@0 559 }
michael@0 560 gScopes = nullptr;
michael@0 561
michael@0 562 // We're forcibly killing scopes, rather than allowing them to go away
michael@0 563 // when they're ready. As such, we need to do some cleanup before they
michael@0 564 // can safely be destroyed.
michael@0 565
michael@0 566 for (cur = gDyingScopes; cur; cur = cur->mNext) {
michael@0 567 // Give the Components object a chance to try to clean up.
michael@0 568 if (cur->mComponents)
michael@0 569 cur->mComponents->SystemIsBeingShutDown();
michael@0 570
michael@0 571 // Walk the protos first. Wrapper shutdown can leave dangling
michael@0 572 // proto pointers in the proto map.
michael@0 573 cur->mWrappedNativeProtoMap->
michael@0 574 Enumerate(WrappedNativeProtoShutdownEnumerator, &data);
michael@0 575 cur->mWrappedNativeMap->
michael@0 576 Enumerate(WrappedNativeShutdownEnumerator, &data);
michael@0 577 }
michael@0 578
michael@0 579 // Now it is safe to kill all the scopes.
michael@0 580 KillDyingScopes();
michael@0 581 }
michael@0 582
michael@0 583
michael@0 584 /***************************************************************************/
michael@0 585
michael@0 586 static PLDHashOperator
michael@0 587 WNProtoRemover(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 588 uint32_t number, void *arg)
michael@0 589 {
michael@0 590 XPCWrappedNativeProtoMap* detachedMap = (XPCWrappedNativeProtoMap*)arg;
michael@0 591
michael@0 592 XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)
michael@0 593 ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value;
michael@0 594
michael@0 595 detachedMap->Add(proto);
michael@0 596
michael@0 597 return PL_DHASH_REMOVE;
michael@0 598 }
michael@0 599
michael@0 600 void
michael@0 601 XPCWrappedNativeScope::RemoveWrappedNativeProtos()
michael@0 602 {
michael@0 603 mWrappedNativeProtoMap->Enumerate(WNProtoRemover,
michael@0 604 GetRuntime()->GetDetachedWrappedNativeProtoMap());
michael@0 605 }
michael@0 606
michael@0 607 JSObject *
michael@0 608 XPCWrappedNativeScope::GetExpandoChain(HandleObject target)
michael@0 609 {
michael@0 610 MOZ_ASSERT(GetObjectScope(target) == this);
michael@0 611 if (!mXrayExpandos.initialized())
michael@0 612 return nullptr;
michael@0 613 return mXrayExpandos.lookup(target);
michael@0 614 }
michael@0 615
michael@0 616 bool
michael@0 617 XPCWrappedNativeScope::SetExpandoChain(JSContext *cx, HandleObject target,
michael@0 618 HandleObject chain)
michael@0 619 {
michael@0 620 MOZ_ASSERT(GetObjectScope(target) == this);
michael@0 621 MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
michael@0 622 MOZ_ASSERT_IF(chain, GetObjectScope(chain) == this);
michael@0 623 if (!mXrayExpandos.initialized() && !mXrayExpandos.init(cx))
michael@0 624 return false;
michael@0 625 return mXrayExpandos.put(cx, target, chain);
michael@0 626 }
michael@0 627
michael@0 628 /***************************************************************************/
michael@0 629
michael@0 630 // static
michael@0 631 void
michael@0 632 XPCWrappedNativeScope::DebugDumpAllScopes(int16_t depth)
michael@0 633 {
michael@0 634 #ifdef DEBUG
michael@0 635 depth-- ;
michael@0 636
michael@0 637 // get scope count.
michael@0 638 int count = 0;
michael@0 639 XPCWrappedNativeScope* cur;
michael@0 640 for (cur = gScopes; cur; cur = cur->mNext)
michael@0 641 count++ ;
michael@0 642
michael@0 643 XPC_LOG_ALWAYS(("chain of %d XPCWrappedNativeScope(s)", count));
michael@0 644 XPC_LOG_INDENT();
michael@0 645 XPC_LOG_ALWAYS(("gDyingScopes @ %x", gDyingScopes));
michael@0 646 if (depth)
michael@0 647 for (cur = gScopes; cur; cur = cur->mNext)
michael@0 648 cur->DebugDump(depth);
michael@0 649 XPC_LOG_OUTDENT();
michael@0 650 #endif
michael@0 651 }
michael@0 652
michael@0 653 #ifdef DEBUG
michael@0 654 static PLDHashOperator
michael@0 655 WrappedNativeMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 656 uint32_t number, void *arg)
michael@0 657 {
michael@0 658 ((Native2WrappedNativeMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg);
michael@0 659 return PL_DHASH_NEXT;
michael@0 660 }
michael@0 661 static PLDHashOperator
michael@0 662 WrappedNativeProtoMapDumpEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 663 uint32_t number, void *arg)
michael@0 664 {
michael@0 665 ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->DebugDump(*(int16_t*)arg);
michael@0 666 return PL_DHASH_NEXT;
michael@0 667 }
michael@0 668 #endif
michael@0 669
michael@0 670 void
michael@0 671 XPCWrappedNativeScope::DebugDump(int16_t depth)
michael@0 672 {
michael@0 673 #ifdef DEBUG
michael@0 674 depth-- ;
michael@0 675 XPC_LOG_ALWAYS(("XPCWrappedNativeScope @ %x", this));
michael@0 676 XPC_LOG_INDENT();
michael@0 677 XPC_LOG_ALWAYS(("mNext @ %x", mNext));
michael@0 678 XPC_LOG_ALWAYS(("mComponents @ %x", mComponents.get()));
michael@0 679 XPC_LOG_ALWAYS(("mGlobalJSObject @ %x", mGlobalJSObject.get()));
michael@0 680
michael@0 681 XPC_LOG_ALWAYS(("mWrappedNativeMap @ %x with %d wrappers(s)", \
michael@0 682 mWrappedNativeMap, \
michael@0 683 mWrappedNativeMap ? mWrappedNativeMap->Count() : 0));
michael@0 684 // iterate contexts...
michael@0 685 if (depth && mWrappedNativeMap && mWrappedNativeMap->Count()) {
michael@0 686 XPC_LOG_INDENT();
michael@0 687 mWrappedNativeMap->Enumerate(WrappedNativeMapDumpEnumerator, &depth);
michael@0 688 XPC_LOG_OUTDENT();
michael@0 689 }
michael@0 690
michael@0 691 XPC_LOG_ALWAYS(("mWrappedNativeProtoMap @ %x with %d protos(s)", \
michael@0 692 mWrappedNativeProtoMap, \
michael@0 693 mWrappedNativeProtoMap ? mWrappedNativeProtoMap->Count() : 0));
michael@0 694 // iterate contexts...
michael@0 695 if (depth && mWrappedNativeProtoMap && mWrappedNativeProtoMap->Count()) {
michael@0 696 XPC_LOG_INDENT();
michael@0 697 mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth);
michael@0 698 XPC_LOG_OUTDENT();
michael@0 699 }
michael@0 700 XPC_LOG_OUTDENT();
michael@0 701 #endif
michael@0 702 }
michael@0 703
michael@0 704 void
michael@0 705 XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo)
michael@0 706 {
michael@0 707 for (XPCWrappedNativeScope *cur = gScopes; cur; cur = cur->mNext)
michael@0 708 cur->AddSizeOfIncludingThis(scopeSizeInfo);
michael@0 709 }
michael@0 710
michael@0 711 void
michael@0 712 XPCWrappedNativeScope::AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo)
michael@0 713 {
michael@0 714 scopeSizeInfo->mScopeAndMapSize += scopeSizeInfo->mMallocSizeOf(this);
michael@0 715 scopeSizeInfo->mScopeAndMapSize +=
michael@0 716 mWrappedNativeMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
michael@0 717 scopeSizeInfo->mScopeAndMapSize +=
michael@0 718 mWrappedNativeProtoMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
michael@0 719
michael@0 720 if (dom::HasProtoAndIfaceCache(mGlobalJSObject)) {
michael@0 721 dom::ProtoAndIfaceCache* cache = dom::GetProtoAndIfaceCache(mGlobalJSObject);
michael@0 722 scopeSizeInfo->mProtoAndIfaceCacheSize +=
michael@0 723 cache->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
michael@0 724 }
michael@0 725
michael@0 726 // There are other XPCWrappedNativeScope members that could be measured;
michael@0 727 // the above ones have been seen by DMD to be worth measuring. More stuff
michael@0 728 // may be added later.
michael@0 729 }

mercurial