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