1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code, released
16 : * March 31, 1998.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1999
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * John Bandhauer <jband@netscape.com> (original author)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /* Class used to manage the wrapped native objects within a JS scope. */
41 :
42 : #include "xpcprivate.h"
43 : #include "XPCWrapper.h"
44 : #include "jsproxy.h"
45 :
46 : /***************************************************************************/
47 :
48 : #ifdef XPC_TRACK_SCOPE_STATS
49 : static int DEBUG_TotalScopeCount;
50 : static int DEBUG_TotalLiveScopeCount;
51 : static int DEBUG_TotalMaxScopeCount;
52 : static int DEBUG_TotalScopeTraversalCount;
53 : static bool DEBUG_DumpedStats;
54 : #endif
55 :
56 : #ifdef DEBUG
57 15475 : static void DEBUG_TrackNewScope(XPCWrappedNativeScope* scope)
58 : {
59 : #ifdef XPC_TRACK_SCOPE_STATS
60 : DEBUG_TotalScopeCount++;
61 : DEBUG_TotalLiveScopeCount++;
62 : if (DEBUG_TotalMaxScopeCount < DEBUG_TotalLiveScopeCount)
63 : DEBUG_TotalMaxScopeCount = DEBUG_TotalLiveScopeCount;
64 : #endif
65 15475 : }
66 :
67 15471 : static void DEBUG_TrackDeleteScope(XPCWrappedNativeScope* scope)
68 : {
69 : #ifdef XPC_TRACK_SCOPE_STATS
70 : DEBUG_TotalLiveScopeCount--;
71 : #endif
72 15471 : }
73 :
74 43529 : static void DEBUG_TrackScopeTraversal()
75 : {
76 : #ifdef XPC_TRACK_SCOPE_STATS
77 : DEBUG_TotalScopeTraversalCount++;
78 : #endif
79 43529 : }
80 :
81 1403 : static void DEBUG_TrackScopeShutdown()
82 : {
83 : #ifdef XPC_TRACK_SCOPE_STATS
84 : if (!DEBUG_DumpedStats) {
85 : DEBUG_DumpedStats = true;
86 : printf("%d XPCWrappedNativeScope(s) were constructed.\n",
87 : DEBUG_TotalScopeCount);
88 :
89 : printf("%d XPCWrappedNativeScopes(s) max alive at one time.\n",
90 : DEBUG_TotalMaxScopeCount);
91 :
92 : printf("%d XPCWrappedNativeScope(s) alive now.\n" ,
93 : DEBUG_TotalLiveScopeCount);
94 :
95 : printf("%d traversals of Scope list.\n",
96 : DEBUG_TotalScopeTraversalCount);
97 : }
98 : #endif
99 1403 : }
100 : #else
101 : #define DEBUG_TrackNewScope(scope) ((void)0)
102 : #define DEBUG_TrackDeleteScope(scope) ((void)0)
103 : #define DEBUG_TrackScopeTraversal() ((void)0)
104 : #define DEBUG_TrackScopeShutdown() ((void)0)
105 : #endif
106 :
107 : /***************************************************************************/
108 :
109 : XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nsnull;
110 : XPCWrappedNativeScope* XPCWrappedNativeScope::gDyingScopes = nsnull;
111 :
112 : // static
113 : XPCWrappedNativeScope*
114 15475 : XPCWrappedNativeScope::GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative)
115 : {
116 :
117 15475 : XPCWrappedNativeScope* scope = FindInJSObjectScope(ccx, aGlobal, true);
118 15475 : if (!scope)
119 15475 : scope = new XPCWrappedNativeScope(ccx, aGlobal, aNative);
120 : else {
121 : // We need to call SetGlobal in order to refresh our cached
122 : // mPrototypeJSObject and to clear mPrototypeNoHelper (so we get a new
123 : // new one if requested in the new scope) in the case where the global
124 : // object is being reused (JS_ClearScope has been called). NOTE: We are
125 : // only called by nsXPConnect::InitClasses.
126 0 : scope->SetGlobal(ccx, aGlobal, aNative);
127 : }
128 15475 : if (js::GetObjectClass(aGlobal)->flags & JSCLASS_XPCONNECT_GLOBAL)
129 : JS_SetReservedSlot(aGlobal,
130 : JSCLASS_GLOBAL_SLOT_COUNT,
131 15195 : PRIVATE_TO_JSVAL(scope));
132 15475 : return scope;
133 : }
134 :
135 15475 : XPCWrappedNativeScope::XPCWrappedNativeScope(XPCCallContext& ccx,
136 : JSObject* aGlobal,
137 : nsISupports* aNative)
138 15475 : : mRuntime(ccx.GetRuntime()),
139 15475 : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)),
140 15475 : mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
141 15475 : mMainThreadWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
142 : mComponents(nsnull),
143 : mNext(nsnull),
144 : mGlobalJSObject(nsnull),
145 : mPrototypeJSObject(nsnull),
146 : mPrototypeNoHelper(nsnull),
147 : mScriptObjectPrincipal(nsnull),
148 77375 : mNewDOMBindingsEnabled(ccx.GetRuntime()->NewDOMBindingsEnabled())
149 : {
150 : // add ourselves to the scopes list
151 : { // scoped lock
152 30950 : XPCAutoLock lock(mRuntime->GetMapLock());
153 :
154 : #ifdef DEBUG
155 181711 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
156 166236 : NS_ASSERTION(aGlobal != cur->GetGlobalJSObject(), "dup object");
157 : #endif
158 :
159 15475 : mNext = gScopes;
160 15475 : gScopes = this;
161 :
162 : // Grab the XPCContext associated with our context.
163 15475 : mContext = XPCContext::GetXPCContext(ccx.GetJSContext());
164 15475 : mContext->AddScope(this);
165 : }
166 :
167 15475 : if (aGlobal)
168 15475 : SetGlobal(ccx, aGlobal, aNative);
169 :
170 15475 : DEBUG_TrackNewScope(this);
171 15475 : MOZ_COUNT_CTOR(XPCWrappedNativeScope);
172 15475 : }
173 :
174 : // static
175 : JSBool
176 0 : XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope *scope)
177 : {
178 0 : for (XPCWrappedNativeScope *cur = gDyingScopes; cur; cur = cur->mNext) {
179 0 : if (scope == cur)
180 0 : return true;
181 : }
182 0 : return false;
183 : }
184 :
185 : void
186 15475 : XPCWrappedNativeScope::SetComponents(nsXPCComponents* aComponents)
187 : {
188 15475 : NS_IF_ADDREF(aComponents);
189 15475 : NS_IF_RELEASE(mComponents);
190 15475 : mComponents = aComponents;
191 15475 : }
192 :
193 : // Dummy JS class to let wrappers w/o an xpc prototype share
194 : // scopes. By doing this we avoid allocating a new scope for every
195 : // wrapper on creation of the wrapper, and most wrappers won't need
196 : // their own scope at all for the lifetime of the wrapper.
197 : // WRAPPER_SLOTS is key here (even though there's never anything
198 : // in the private data slot in these prototypes), as the number of
199 : // reserved slots in this class needs to match that of the wrappers
200 : // for the JS engine to share scopes.
201 :
202 : js::Class XPC_WN_NoHelper_Proto_JSClass = {
203 : "XPC_WN_NoHelper_Proto_JSClass",// name;
204 : WRAPPER_SLOTS, // flags;
205 :
206 : /* Mandatory non-null function pointer members. */
207 : JS_PropertyStub, // addProperty;
208 : JS_PropertyStub, // delProperty;
209 : JS_PropertyStub, // getProperty;
210 : JS_StrictPropertyStub, // setProperty;
211 : JS_EnumerateStub, // enumerate;
212 : JS_ResolveStub, // resolve;
213 : JS_ConvertStub, // convert;
214 : nsnull, // finalize;
215 :
216 : /* Optionally non-null members start here. */
217 : nsnull, // checkAccess;
218 : nsnull, // call;
219 : nsnull, // construct;
220 : nsnull, // hasInstance;
221 : nsnull, // trace;
222 :
223 : JS_NULL_CLASS_EXT,
224 : XPC_WN_NoCall_ObjectOps
225 : };
226 :
227 :
228 : void
229 15475 : XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal,
230 : nsISupports* aNative)
231 : {
232 : // We allow for calling this more than once. This feature is used by
233 : // nsXPConnect::InitClassesWithNewWrappedGlobal.
234 :
235 15475 : mGlobalJSObject = aGlobal;
236 15475 : mScriptObjectPrincipal = nsnull;
237 :
238 : // Try to find the native global object. If we didn't receive it explicitly,
239 : // we might be able to find it in the private slot.
240 15475 : nsISupports* native = aNative;
241 17733 : if (!native &&
242 2258 : !(~js::GetObjectJSClass(aGlobal)->flags & (JSCLASS_HAS_PRIVATE |
243 2258 : JSCLASS_PRIVATE_IS_NSISUPPORTS)))
244 : {
245 : // Get the private. It might be a WN, in which case we dig deeper.
246 1978 : native = (nsISupports*)xpc_GetJSPrivate(aGlobal);
247 3956 : nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native);
248 1978 : if (wn)
249 0 : native = static_cast<XPCWrappedNative*>(native)->GetIdentityObject();
250 : }
251 :
252 : // Now init our script object principal, if the new global has one.
253 30950 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native);
254 15475 : mScriptObjectPrincipal = sop;
255 :
256 : // Lookup 'globalObject.Object.prototype' for our wrapper's proto
257 : JSObject *objectPrototype =
258 15475 : JS_GetObjectPrototype(ccx.GetJSContext(), aGlobal);
259 15475 : if (objectPrototype)
260 15475 : mPrototypeJSObject = objectPrototype;
261 : else
262 0 : NS_ERROR("Can't get globalObject.Object.prototype");
263 :
264 : // Clear the no helper wrapper prototype object so that a new one
265 : // gets created if needed.
266 15475 : mPrototypeNoHelper = nsnull;
267 15475 : }
268 :
269 46413 : XPCWrappedNativeScope::~XPCWrappedNativeScope()
270 : {
271 15471 : MOZ_COUNT_DTOR(XPCWrappedNativeScope);
272 15471 : DEBUG_TrackDeleteScope(this);
273 :
274 : // We can do additional cleanup assertions here...
275 :
276 15471 : if (mWrappedNativeMap) {
277 15471 : NS_ASSERTION(0 == mWrappedNativeMap->Count(), "scope has non-empty map");
278 15471 : delete mWrappedNativeMap;
279 : }
280 :
281 15471 : if (mWrappedNativeProtoMap) {
282 15471 : NS_ASSERTION(0 == mWrappedNativeProtoMap->Count(), "scope has non-empty map");
283 15471 : delete mWrappedNativeProtoMap;
284 : }
285 :
286 15471 : if (mMainThreadWrappedNativeProtoMap) {
287 15471 : NS_ASSERTION(0 == mMainThreadWrappedNativeProtoMap->Count(), "scope has non-empty map");
288 15471 : delete mMainThreadWrappedNativeProtoMap;
289 : }
290 :
291 15471 : if (mContext)
292 1639 : mContext->RemoveScope(this);
293 :
294 : // XXX we should assert that we are dead or that xpconnect has shutdown
295 : // XXX might not want to do this at xpconnect shutdown time???
296 15471 : NS_IF_RELEASE(mComponents);
297 :
298 15471 : JSRuntime *rt = mRuntime->GetJSRuntime();
299 15471 : mGlobalJSObject.finalize(rt);
300 15471 : mPrototypeJSObject.finalize(rt);
301 61884 : }
302 :
303 : JSObject *
304 882319 : XPCWrappedNativeScope::GetPrototypeNoHelper(XPCCallContext& ccx)
305 : {
306 : // We could create this prototype in SetGlobal(), but all scopes
307 : // don't need one, so we save ourselves a bit of space if we
308 : // create these when they're needed.
309 882319 : if (!mPrototypeNoHelper) {
310 : mPrototypeNoHelper =
311 : xpc_NewSystemInheritingJSObject(ccx,
312 : js::Jsvalify(&XPC_WN_NoHelper_Proto_JSClass),
313 : mPrototypeJSObject,
314 13639 : false, mGlobalJSObject);
315 :
316 13639 : NS_ASSERTION(mPrototypeNoHelper,
317 : "Failed to create prototype for wrappers w/o a helper");
318 : } else {
319 868680 : xpc_UnmarkGrayObject(mPrototypeNoHelper);
320 : }
321 :
322 882319 : return mPrototypeNoHelper;
323 : }
324 :
325 : static JSDHashOperator
326 3488506 : WrappedNativeJSGCThingTracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
327 : uint32_t number, void *arg)
328 : {
329 3488506 : XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
330 3488506 : if (wrapper->HasExternalReference() && !wrapper->IsWrapperExpired()) {
331 14701 : JSTracer* trc = (JSTracer *)arg;
332 14701 : JS_CALL_OBJECT_TRACER(trc, wrapper->GetFlatJSObjectPreserveColor(),
333 : "XPCWrappedNative::mFlatJSObject");
334 : }
335 :
336 3488506 : return JS_DHASH_NEXT;
337 : }
338 :
339 : // static
340 : void
341 14728 : XPCWrappedNativeScope::TraceJS(JSTracer* trc, XPCJSRuntime* rt)
342 : {
343 : // FIXME The lock may not be necessary during tracing as that serializes
344 : // access to JS runtime. See bug 380139.
345 29456 : XPCAutoLock lock(rt->GetMapLock());
346 :
347 : // Do JS_CallTracer for all wrapped natives with external references.
348 176270 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
349 161542 : cur->mWrappedNativeMap->Enumerate(WrappedNativeJSGCThingTracer, trc);
350 : }
351 14728 : }
352 :
353 : static JSDHashOperator
354 317990 : WrappedNativeSuspecter(JSDHashTable *table, JSDHashEntryHdr *hdr,
355 : uint32_t number, void *arg)
356 : {
357 317990 : XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
358 :
359 317990 : if (wrapper->HasExternalReference()) {
360 : nsCycleCollectionTraversalCallback *cb =
361 190 : static_cast<nsCycleCollectionTraversalCallback *>(arg);
362 190 : XPCJSRuntime::SuspectWrappedNative(wrapper, *cb);
363 : }
364 :
365 317990 : return JS_DHASH_NEXT;
366 : }
367 :
368 : // static
369 : void
370 1910 : XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt,
371 : nsCycleCollectionTraversalCallback& cb)
372 : {
373 3820 : XPCAutoLock lock(rt->GetMapLock());
374 :
375 22982 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
376 21072 : cur->mWrappedNativeMap->Enumerate(WrappedNativeSuspecter, &cb);
377 : }
378 1910 : }
379 :
380 : // static
381 : void
382 14728 : XPCWrappedNativeScope::FinishedMarkPhaseOfGC(JSContext* cx, XPCJSRuntime* rt)
383 : {
384 : // FIXME The lock may not be necessary since we are inside JSGC_MARK_END
385 : // callback and GX serializes access to JS runtime. See bug 380139.
386 29456 : XPCAutoLock lock(rt->GetMapLock());
387 :
388 : // We are in JSGC_MARK_END and JSGC_FINALIZE_END must always follow it
389 : // calling FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
390 : // KillDyingScopes.
391 14728 : NS_ASSERTION(gDyingScopes == nsnull,
392 : "JSGC_MARK_END without JSGC_FINALIZE_END");
393 :
394 14728 : XPCWrappedNativeScope* prev = nsnull;
395 14728 : XPCWrappedNativeScope* cur = gScopes;
396 :
397 190998 : while (cur) {
398 161542 : XPCWrappedNativeScope* next = cur->mNext;
399 :
400 323084 : if (cur->mGlobalJSObject &&
401 323084 : JS_IsAboutToBeFinalized(cur->mGlobalJSObject)) {
402 15447 : cur->mGlobalJSObject.finalize(cx);
403 15447 : cur->mScriptObjectPrincipal = nsnull;
404 15447 : if (cur->GetCachedDOMPrototypes().IsInitialized())
405 259 : cur->GetCachedDOMPrototypes().Clear();
406 : // Move this scope from the live list to the dying list.
407 15447 : if (prev)
408 4007 : prev->mNext = next;
409 : else
410 11440 : gScopes = next;
411 15447 : cur->mNext = gDyingScopes;
412 15447 : gDyingScopes = cur;
413 15447 : cur = nsnull;
414 : } else {
415 292190 : if (cur->mPrototypeJSObject &&
416 292190 : JS_IsAboutToBeFinalized(cur->mPrototypeJSObject)) {
417 0 : cur->mPrototypeJSObject.finalize(cx);
418 : }
419 270250 : if (cur->mPrototypeNoHelper &&
420 124155 : JS_IsAboutToBeFinalized(cur->mPrototypeNoHelper)) {
421 1898 : cur->mPrototypeNoHelper = nsnull;
422 : }
423 : }
424 161542 : if (cur)
425 146095 : prev = cur;
426 161542 : cur = next;
427 : }
428 14728 : }
429 :
430 : // static
431 : void
432 14728 : XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(JSContext* cx)
433 : {
434 14728 : XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
435 :
436 : // FIXME The lock may not be necessary since we are inside
437 : // JSGC_FINALIZE_END callback and at this point GC still serializes access
438 : // to JS runtime. See bug 380139.
439 29456 : XPCAutoLock lock(rt->GetMapLock());
440 14728 : KillDyingScopes();
441 14728 : }
442 :
443 : static JSDHashOperator
444 2233014 : WrappedNativeMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
445 : uint32_t number_t, void *arg)
446 : {
447 2233014 : ((Native2WrappedNativeMap::Entry*)hdr)->value->Mark();
448 2233014 : return JS_DHASH_NEXT;
449 : }
450 :
451 : // We need to explicitly mark all the protos too because some protos may be
452 : // alive in the hashtable but not currently in use by any wrapper
453 : static JSDHashOperator
454 878482 : WrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
455 : uint32_t number, void *arg)
456 : {
457 878482 : ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->Mark();
458 878482 : return JS_DHASH_NEXT;
459 : }
460 :
461 : // static
462 : void
463 14728 : XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos()
464 : {
465 160823 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
466 146095 : cur->mWrappedNativeMap->Enumerate(WrappedNativeMarker, nsnull);
467 146095 : cur->mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMarker, nsnull);
468 146095 : cur->mMainThreadWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMarker, nsnull);
469 : }
470 :
471 14728 : DEBUG_TrackScopeTraversal();
472 14728 : }
473 :
474 : #ifdef DEBUG
475 : static JSDHashOperator
476 2233014 : ASSERT_WrappedNativeSetNotMarked(JSDHashTable *table, JSDHashEntryHdr *hdr,
477 : uint32_t number, void *arg)
478 : {
479 2233014 : ((Native2WrappedNativeMap::Entry*)hdr)->value->ASSERT_SetsNotMarked();
480 2233014 : return JS_DHASH_NEXT;
481 : }
482 :
483 : static JSDHashOperator
484 878482 : ASSERT_WrappedNativeProtoSetNotMarked(JSDHashTable *table, JSDHashEntryHdr *hdr,
485 : uint32_t number, void *arg)
486 : {
487 878482 : ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->ASSERT_SetNotMarked();
488 878482 : return JS_DHASH_NEXT;
489 : }
490 :
491 : // static
492 : void
493 14728 : XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked()
494 : {
495 160823 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
496 146095 : cur->mWrappedNativeMap->Enumerate(ASSERT_WrappedNativeSetNotMarked, nsnull);
497 146095 : cur->mWrappedNativeProtoMap->Enumerate(ASSERT_WrappedNativeProtoSetNotMarked, nsnull);
498 146095 : cur->mMainThreadWrappedNativeProtoMap->Enumerate(ASSERT_WrappedNativeProtoSetNotMarked, nsnull);
499 : }
500 14728 : }
501 : #endif
502 :
503 : static JSDHashOperator
504 2233014 : WrappedNativeTearoffSweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
505 : uint32_t number, void *arg)
506 : {
507 2233014 : ((Native2WrappedNativeMap::Entry*)hdr)->value->SweepTearOffs();
508 2233014 : return JS_DHASH_NEXT;
509 : }
510 :
511 : // static
512 : void
513 11922 : XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs()
514 : {
515 158017 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
516 146095 : cur->mWrappedNativeMap->Enumerate(WrappedNativeTearoffSweeper, nsnull);
517 :
518 11922 : DEBUG_TrackScopeTraversal();
519 11922 : }
520 :
521 : // static
522 : void
523 16131 : XPCWrappedNativeScope::KillDyingScopes()
524 : {
525 : // always called inside the lock!
526 16131 : XPCWrappedNativeScope* cur = gDyingScopes;
527 47733 : while (cur) {
528 15471 : XPCWrappedNativeScope* next = cur->mNext;
529 15471 : delete cur;
530 15471 : cur = next;
531 : }
532 16131 : gDyingScopes = nsnull;
533 16131 : }
534 :
535 : struct ShutdownData
536 : {
537 1403 : ShutdownData()
538 : : wrapperCount(0),
539 1403 : protoCount(0) {}
540 : int wrapperCount;
541 : int protoCount;
542 : };
543 :
544 : static JSDHashOperator
545 781 : WrappedNativeShutdownEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
546 : uint32_t number, void *arg)
547 : {
548 781 : ShutdownData* data = (ShutdownData*) arg;
549 781 : XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
550 :
551 781 : if (wrapper->IsValid()) {
552 781 : wrapper->SystemIsBeingShutDown();
553 781 : data->wrapperCount++;
554 : }
555 781 : return JS_DHASH_REMOVE;
556 : }
557 :
558 : static JSDHashOperator
559 126 : WrappedNativeProtoShutdownEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
560 : uint32_t number, void *arg)
561 : {
562 126 : ShutdownData* data = (ShutdownData*) arg;
563 : ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->
564 126 : SystemIsBeingShutDown();
565 126 : data->protoCount++;
566 126 : return JS_DHASH_REMOVE;
567 : }
568 :
569 : //static
570 : void
571 1403 : XPCWrappedNativeScope::SystemIsBeingShutDown()
572 : {
573 1403 : DEBUG_TrackScopeTraversal();
574 1403 : DEBUG_TrackScopeShutdown();
575 :
576 1403 : int liveScopeCount = 0;
577 :
578 1403 : ShutdownData data;
579 :
580 : XPCWrappedNativeScope* cur;
581 :
582 : // First move all the scopes to the dying list.
583 :
584 1403 : cur = gScopes;
585 2830 : while (cur) {
586 24 : XPCWrappedNativeScope* next = cur->mNext;
587 24 : cur->mNext = gDyingScopes;
588 24 : gDyingScopes = cur;
589 24 : cur = next;
590 24 : liveScopeCount++;
591 : }
592 1403 : gScopes = nsnull;
593 :
594 : // Walk the unified dying list and call shutdown on all wrappers and protos
595 :
596 1427 : for (cur = gDyingScopes; cur; cur = cur->mNext) {
597 : // Give the Components object a chance to try to clean up.
598 24 : if (cur->mComponents)
599 24 : cur->mComponents->SystemIsBeingShutDown();
600 :
601 : // Walk the protos first. Wrapper shutdown can leave dangling
602 : // proto pointers in the proto map.
603 : cur->mWrappedNativeProtoMap->
604 24 : Enumerate(WrappedNativeProtoShutdownEnumerator, &data);
605 : cur->mMainThreadWrappedNativeProtoMap->
606 24 : Enumerate(WrappedNativeProtoShutdownEnumerator, &data);
607 : cur->mWrappedNativeMap->
608 24 : Enumerate(WrappedNativeShutdownEnumerator, &data);
609 : }
610 :
611 : // Now it is safe to kill all the scopes.
612 1403 : KillDyingScopes();
613 :
614 : #ifdef XPC_DUMP_AT_SHUTDOWN
615 : if (data.wrapperCount)
616 : printf("deleting nsXPConnect with %d live XPCWrappedNatives\n",
617 : data.wrapperCount);
618 : if (data.protoCount)
619 : printf("deleting nsXPConnect with %d live XPCWrappedNativeProtos\n",
620 : data.protoCount);
621 : if (liveScopeCount)
622 : printf("deleting nsXPConnect with %d live XPCWrappedNativeScopes\n",
623 : liveScopeCount);
624 : #endif
625 1403 : }
626 :
627 :
628 : /***************************************************************************/
629 :
630 : static
631 : XPCWrappedNativeScope*
632 2156634 : GetScopeOfObject(JSObject* obj)
633 : {
634 : nsISupports* supports;
635 2156634 : js::Class* clazz = js::GetObjectClass(obj);
636 2156634 : JSBool isWrapper = IS_WRAPPER_CLASS(clazz);
637 :
638 2156634 : if (isWrapper && IS_SLIM_WRAPPER_OBJECT(obj))
639 7457 : return GetSlimWrapperProto(obj)->GetScope();
640 :
641 2149177 : if (!isWrapper || !(supports = (nsISupports*) xpc_GetJSPrivate(obj)))
642 610714 : return nsnull;
643 :
644 : #ifdef DEBUG
645 : {
646 3076926 : nsCOMPtr<nsIXPConnectWrappedNative> iface = do_QueryInterface(supports);
647 :
648 1538463 : NS_ASSERTION(iface, "Uh, how'd this happen?");
649 : }
650 : #endif
651 :
652 : // obj is one of our nsXPConnectWrappedNative objects.
653 1538463 : return ((XPCWrappedNative*)supports)->GetScope();
654 : }
655 :
656 :
657 : #ifdef DEBUG
658 0 : void DEBUG_CheckForComponentsInScope(JSContext* cx, JSObject* obj,
659 : JSObject* startingObj,
660 : JSBool OKIfNotInitialized,
661 : XPCJSRuntime* runtime)
662 : {
663 0 : if (OKIfNotInitialized)
664 0 : return;
665 :
666 0 : if (!(JS_GetOptions(cx) & JSOPTION_PRIVATE_IS_NSISUPPORTS))
667 0 : return;
668 :
669 0 : const char* name = runtime->GetStringName(XPCJSRuntime::IDX_COMPONENTS);
670 : jsval prop;
671 0 : if (JS_LookupProperty(cx, obj, name, &prop) && !JSVAL_IS_PRIMITIVE(prop))
672 0 : return;
673 :
674 : // This is pretty much always bad. It usually means that native code is
675 : // making a callback to an interface implemented in JavaScript, but the
676 : // document where the JS object was created has already been cleared and the
677 : // global properties of that document's window are *gone*. Generally this
678 : // indicates a problem that should be addressed in the design and use of the
679 : // callback code.
680 0 : NS_ERROR("XPConnect is being called on a scope without a 'Components' property! (stack and details follow)");
681 0 : printf("The current JS stack is:\n");
682 0 : xpc_DumpJSStack(cx, true, true, true);
683 :
684 0 : printf("And the object whose scope lacks a 'Components' property is:\n");
685 0 : js_DumpObject(startingObj);
686 :
687 0 : JSObject *p = startingObj;
688 0 : while (js::IsWrapper(p)) {
689 0 : p = js::GetProxyPrivate(p).toObjectOrNull();
690 0 : if (!p)
691 0 : break;
692 0 : printf("which is a wrapper for:\n");
693 0 : js_DumpObject(p);
694 : }
695 : }
696 : #else
697 : #define DEBUG_CheckForComponentsInScope(ccx, obj, startingObj, OKIfNotInitialized, runtime) \
698 : ((void)0)
699 : #endif
700 :
701 : // static
702 : XPCWrappedNativeScope*
703 2156634 : XPCWrappedNativeScope::FindInJSObjectScope(JSContext* cx, JSObject* obj,
704 : JSBool OKIfNotInitialized,
705 : XPCJSRuntime* runtime)
706 : {
707 : XPCWrappedNativeScope* scope;
708 :
709 2156634 : if (!obj)
710 0 : return nsnull;
711 :
712 : // If this object is itself a wrapped native then we can get the
713 : // scope directly.
714 :
715 2156634 : scope = GetScopeOfObject(obj);
716 2156634 : if (scope)
717 1545920 : return scope;
718 :
719 : // Else we'll have to look up the parent chain to get the scope
720 :
721 1221428 : JSAutoEnterCompartment ac;
722 610714 : ac.enterAndIgnoreErrors(cx, obj);
723 :
724 : #ifdef DEBUG
725 610714 : JSObject *startingObj = obj;
726 : #endif
727 :
728 610714 : obj = JS_GetGlobalForObject(cx, obj);
729 :
730 610714 : if (js::GetObjectClass(obj)->flags & JSCLASS_XPCONNECT_GLOBAL) {
731 610434 : scope = XPCWrappedNativeScope::GetNativeScope(obj);
732 610434 : if (scope)
733 595239 : return scope;
734 : }
735 :
736 15475 : if (!runtime) {
737 0 : runtime = nsXPConnect::GetRuntimeInstance();
738 0 : NS_ASSERTION(runtime, "This should never be null!");
739 : }
740 :
741 : // XXX We are assuming that the scope count is low enough that traversing
742 : // the linked list is more reasonable then doing a hashtable lookup.
743 15475 : XPCWrappedNativeScope* found = nsnull;
744 : { // scoped lock
745 30950 : XPCAutoLock lock(runtime->GetMapLock());
746 :
747 15475 : DEBUG_TrackScopeTraversal();
748 :
749 181711 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
750 166236 : if (obj == cur->GetGlobalJSObject()) {
751 0 : found = cur;
752 0 : break;
753 : }
754 : }
755 : }
756 :
757 15475 : if (found) {
758 : // This cannot be called within the map lock!
759 : DEBUG_CheckForComponentsInScope(cx, obj, startingObj,
760 0 : OKIfNotInitialized, runtime);
761 0 : return found;
762 : }
763 :
764 : // Failure to find the scope is only OK if the caller told us it might fail.
765 : // This flag would only be set in the call from
766 : // XPCWrappedNativeScope::GetNewOrUsed
767 15475 : NS_ASSERTION(OKIfNotInitialized, "No scope has this global object!");
768 15475 : return nsnull;
769 : }
770 :
771 : /***************************************************************************/
772 :
773 : static JSDHashOperator
774 50 : WNProtoSecPolicyClearer(JSDHashTable *table, JSDHashEntryHdr *hdr,
775 : uint32_t number, void *arg)
776 : {
777 : XPCWrappedNativeProto* proto =
778 50 : ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value;
779 50 : *(proto->GetSecurityInfoAddr()) = nsnull;
780 50 : return JS_DHASH_NEXT;
781 : }
782 :
783 : // static
784 : nsresult
785 1 : XPCWrappedNativeScope::ClearAllWrappedNativeSecurityPolicies(XPCCallContext& ccx)
786 : {
787 : // Hold the lock throughout.
788 2 : XPCAutoLock lock(ccx.GetRuntime()->GetMapLock());
789 :
790 7 : for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
791 6 : cur->mWrappedNativeProtoMap->Enumerate(WNProtoSecPolicyClearer, nsnull);
792 6 : cur->mMainThreadWrappedNativeProtoMap->Enumerate(WNProtoSecPolicyClearer, nsnull);
793 : }
794 :
795 1 : DEBUG_TrackScopeTraversal();
796 :
797 1 : return NS_OK;
798 : }
799 :
800 : static JSDHashOperator
801 0 : WNProtoRemover(JSDHashTable *table, JSDHashEntryHdr *hdr,
802 : uint32_t number, void *arg)
803 : {
804 0 : XPCWrappedNativeProtoMap* detachedMap = (XPCWrappedNativeProtoMap*)arg;
805 :
806 : XPCWrappedNativeProto* proto = (XPCWrappedNativeProto*)
807 0 : ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value;
808 :
809 0 : detachedMap->Add(proto);
810 :
811 0 : return JS_DHASH_REMOVE;
812 : }
813 :
814 : void
815 2258 : XPCWrappedNativeScope::RemoveWrappedNativeProtos()
816 : {
817 4516 : XPCAutoLock al(mRuntime->GetMapLock());
818 :
819 : mWrappedNativeProtoMap->Enumerate(WNProtoRemover,
820 2258 : GetRuntime()->GetDetachedWrappedNativeProtoMap());
821 : mMainThreadWrappedNativeProtoMap->Enumerate(WNProtoRemover,
822 2258 : GetRuntime()->GetDetachedWrappedNativeProtoMap());
823 2258 : }
824 :
825 : static PLDHashOperator
826 2062 : TraceDOMPrototype(const char* aKey, JSObject* aData, void* aClosure)
827 : {
828 2062 : JSTracer *trc = static_cast<JSTracer*>(aClosure);
829 2062 : JS_CALL_OBJECT_TRACER(trc, aData, "DOM prototype");
830 2062 : return PL_DHASH_NEXT;
831 : }
832 :
833 : void
834 147064 : XPCWrappedNativeScope::TraceDOMPrototypes(JSTracer *trc)
835 : {
836 147064 : if (mCachedDOMPrototypes.IsInitialized()) {
837 1935 : mCachedDOMPrototypes.EnumerateRead(TraceDOMPrototype, trc);
838 : }
839 147064 : }
840 :
841 : /***************************************************************************/
842 :
843 : // static
844 : void
845 0 : XPCWrappedNativeScope::DebugDumpAllScopes(PRInt16 depth)
846 : {
847 : #ifdef DEBUG
848 0 : depth-- ;
849 :
850 : // get scope count.
851 0 : int count = 0;
852 : XPCWrappedNativeScope* cur;
853 0 : for (cur = gScopes; cur; cur = cur->mNext)
854 0 : count++ ;
855 :
856 0 : XPC_LOG_ALWAYS(("chain of %d XPCWrappedNativeScope(s)", count));
857 0 : XPC_LOG_INDENT();
858 0 : XPC_LOG_ALWAYS(("gDyingScopes @ %x", gDyingScopes));
859 0 : if (depth)
860 0 : for (cur = gScopes; cur; cur = cur->mNext)
861 0 : cur->DebugDump(depth);
862 0 : XPC_LOG_OUTDENT();
863 : #endif
864 0 : }
865 :
866 : #ifdef DEBUG
867 : static JSDHashOperator
868 0 : WrappedNativeMapDumpEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
869 : uint32_t number, void *arg)
870 : {
871 0 : ((Native2WrappedNativeMap::Entry*)hdr)->value->DebugDump(*(PRInt16*)arg);
872 0 : return JS_DHASH_NEXT;
873 : }
874 : static JSDHashOperator
875 0 : WrappedNativeProtoMapDumpEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
876 : uint32_t number, void *arg)
877 : {
878 0 : ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->DebugDump(*(PRInt16*)arg);
879 0 : return JS_DHASH_NEXT;
880 : }
881 : #endif
882 :
883 : void
884 0 : XPCWrappedNativeScope::DebugDump(PRInt16 depth)
885 : {
886 : #ifdef DEBUG
887 0 : depth-- ;
888 0 : XPC_LOG_ALWAYS(("XPCWrappedNativeScope @ %x", this));
889 0 : XPC_LOG_INDENT();
890 0 : XPC_LOG_ALWAYS(("mRuntime @ %x", mRuntime));
891 0 : XPC_LOG_ALWAYS(("mNext @ %x", mNext));
892 0 : XPC_LOG_ALWAYS(("mComponents @ %x", mComponents));
893 0 : XPC_LOG_ALWAYS(("mGlobalJSObject @ %x", mGlobalJSObject.get()));
894 0 : XPC_LOG_ALWAYS(("mPrototypeJSObject @ %x", mPrototypeJSObject.get()));
895 0 : XPC_LOG_ALWAYS(("mPrototypeNoHelper @ %x", mPrototypeNoHelper));
896 :
897 0 : XPC_LOG_ALWAYS(("mWrappedNativeMap @ %x with %d wrappers(s)", \
898 : mWrappedNativeMap, \
899 : mWrappedNativeMap ? mWrappedNativeMap->Count() : 0));
900 : // iterate contexts...
901 0 : if (depth && mWrappedNativeMap && mWrappedNativeMap->Count()) {
902 0 : XPC_LOG_INDENT();
903 0 : mWrappedNativeMap->Enumerate(WrappedNativeMapDumpEnumerator, &depth);
904 0 : XPC_LOG_OUTDENT();
905 : }
906 :
907 0 : XPC_LOG_ALWAYS(("mWrappedNativeProtoMap @ %x with %d protos(s)", \
908 : mWrappedNativeProtoMap, \
909 : mWrappedNativeProtoMap ? mWrappedNativeProtoMap->Count() : 0));
910 : // iterate contexts...
911 0 : if (depth && mWrappedNativeProtoMap && mWrappedNativeProtoMap->Count()) {
912 0 : XPC_LOG_INDENT();
913 0 : mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth);
914 0 : XPC_LOG_OUTDENT();
915 : }
916 :
917 0 : XPC_LOG_ALWAYS(("mMainThreadWrappedNativeProtoMap @ %x with %d protos(s)", \
918 : mMainThreadWrappedNativeProtoMap, \
919 : mMainThreadWrappedNativeProtoMap ? mMainThreadWrappedNativeProtoMap->Count() : 0));
920 : // iterate contexts...
921 0 : if (depth && mMainThreadWrappedNativeProtoMap && mMainThreadWrappedNativeProtoMap->Count()) {
922 0 : XPC_LOG_INDENT();
923 0 : mMainThreadWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth);
924 0 : XPC_LOG_OUTDENT();
925 : }
926 0 : XPC_LOG_OUTDENT();
927 : #endif
928 0 : }
929 :
930 : size_t
931 3 : XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(nsMallocSizeOfFun mallocSizeOf)
932 : {
933 3 : XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
934 6 : XPCAutoLock lock(rt->GetMapLock());
935 :
936 3 : size_t n = 0;
937 62 : for (XPCWrappedNativeScope *cur = gScopes; cur; cur = cur->mNext) {
938 59 : n += cur->SizeOfIncludingThis(mallocSizeOf);
939 : }
940 3 : return n;
941 : }
942 :
943 : size_t
944 59 : XPCWrappedNativeScope::SizeOfIncludingThis(nsMallocSizeOfFun mallocSizeOf)
945 : {
946 59 : size_t n = 0;
947 59 : n += mallocSizeOf(this);
948 59 : n += mWrappedNativeMap->SizeOfIncludingThis(mallocSizeOf);
949 59 : n += mWrappedNativeProtoMap->SizeOfIncludingThis(mallocSizeOf);
950 59 : n += mMainThreadWrappedNativeProtoMap->SizeOfIncludingThis(mallocSizeOf);
951 :
952 : // There are other XPCWrappedNativeScope members that could be measured;
953 : // the above ones have been seen by DMD to be worth measuring. More stuff
954 : // may be added later.
955 :
956 59 : return n;
957 : }
958 :
959 :
|