Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
7 /* Class that wraps JS objects to appear as XPCOM objects. */
9 #include "xpcprivate.h"
10 #include "jsprf.h"
11 #include "nsCCUncollectableMarker.h"
12 #include "nsCxPusher.h"
13 #include "nsContentUtils.h"
14 #include "nsThreadUtils.h"
16 using namespace mozilla;
18 // NOTE: much of the fancy footwork is done in xpcstubs.cpp
21 // nsXPCWrappedJS lifetime.
22 //
23 // An nsXPCWrappedJS is either rooting its JS object or is subject to finalization.
24 // The subject-to-finalization state lets wrappers support
25 // nsSupportsWeakReference in the case where the underlying JS object
26 // is strongly owned, but the wrapper itself is only weakly owned.
27 //
28 // A wrapper is rooting its JS object whenever its refcount is greater than 1. In
29 // this state, root wrappers are always added to the cycle collector graph. The
30 // wrapper keeps around an extra refcount, added in the constructor, to support
31 // the possibility of an eventual transition to the subject-to-finalization state.
32 // This extra refcount is ignored by the cycle collector, which traverses the "self"
33 // edge for this refcount.
34 //
35 // When the refcount of a rooting wrapper drops to 1, if there is no weak reference
36 // to the wrapper (which can only happen for the root wrapper), it is immediately
37 // Destroy()'d. Otherwise, it becomes subject to finalization.
38 //
39 // When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is
40 // now owned exclusively by its JS object. Either a weak reference will be turned into
41 // a strong ref which will bring its refcount up to 2 and change the wrapper back to
42 // the rooting state, or it will stay alive until the JS object dies. If the JS object
43 // dies, then when XPCJSRuntime::FinalizeCallback calls FindDyingJSObjects
44 // it will find the wrapper and call Release() in it, destroying the wrapper.
45 // Otherwise, the wrapper will stay alive, even if it no longer has a weak reference
46 // to it.
47 //
48 // When the wrapper is subject to finalization, it is kept alive by an implicit reference
49 // from the JS object which is invisible to the cycle collector, so the cycle collector
50 // does not traverse any children of wrappers that are subject to finalization. This will
51 // result in a leak if a wrapper in the non-rooting state has an aggregated native that
52 // keeps alive the wrapper's JS object. See bug 947049.
55 // If traversing wrappedJS wouldn't release it, nor cause any other objects to be
56 // added to the graph, there is no need to add it to the graph at all.
57 bool
58 nsXPCWrappedJS::CanSkip()
59 {
60 if (!nsCCUncollectableMarker::sGeneration)
61 return false;
63 if (IsSubjectToFinalization())
64 return true;
66 // If this wrapper holds a gray object, need to trace it.
67 JSObject *obj = GetJSObjectPreserveColor();
68 if (obj && xpc_IsGrayGCThing(obj))
69 return false;
71 // For non-root wrappers, check if the root wrapper will be
72 // added to the CC graph.
73 if (!IsRootWrapper())
74 return mRoot->CanSkip();
76 // For the root wrapper, check if there is an aggregated
77 // native object that will be added to the CC graph.
78 if (!IsAggregatedToNative())
79 return true;
81 nsISupports* agg = GetAggregatedNativeObject();
82 nsXPCOMCycleCollectionParticipant* cp = nullptr;
83 CallQueryInterface(agg, &cp);
84 nsISupports* canonical = nullptr;
85 agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
86 reinterpret_cast<void**>(&canonical));
87 return cp && canonical && cp->CanSkipThis(canonical);
88 }
90 NS_IMETHODIMP
91 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
92 (void *p, nsCycleCollectionTraversalCallback &cb)
93 {
94 nsISupports *s = static_cast<nsISupports*>(p);
95 MOZ_ASSERT(CheckForRightISupports(s), "not the nsISupports pointer we expect");
96 nsXPCWrappedJS *tmp = Downcast(s);
98 nsrefcnt refcnt = tmp->mRefCnt.get();
99 if (cb.WantDebugInfo()) {
100 char name[72];
101 if (tmp->GetClass())
102 JS_snprintf(name, sizeof(name), "nsXPCWrappedJS (%s)",
103 tmp->GetClass()->GetInterfaceName());
104 else
105 JS_snprintf(name, sizeof(name), "nsXPCWrappedJS");
106 cb.DescribeRefCountedNode(refcnt, name);
107 } else {
108 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt)
109 }
111 // A wrapper that is subject to finalization will only die when its JS object dies.
112 if (tmp->IsSubjectToFinalization())
113 return NS_OK;
115 // Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is
116 // not subject to finalization alive.
117 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
118 cb.NoteXPCOMChild(s);
120 if (tmp->IsValid()) {
121 MOZ_ASSERT(refcnt > 1);
122 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
123 cb.NoteJSChild(tmp->GetJSObjectPreserveColor());
124 }
126 if (tmp->IsRootWrapper()) {
127 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
128 cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
129 } else {
130 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
131 cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper()));
132 }
134 return NS_OK;
135 }
137 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
139 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
140 tmp->Unlink();
141 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
143 // XPCJSRuntime keeps a table of WJS, so we can remove them from
144 // the purple buffer in between CCs.
145 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS)
146 return true;
147 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
149 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS)
150 return tmp->CanSkip();
151 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
153 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS)
154 return tmp->CanSkip();
155 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
157 NS_IMETHODIMP
158 nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
159 {
160 MOZ_ASSERT(IsAggregatedToNative(), "bad AggregatedQueryInterface call");
162 if (!IsValid())
163 return NS_ERROR_UNEXPECTED;
165 // Put this here rather that in DelegatedQueryInterface because it needs
166 // to be in QueryInterface before the possible delegation to 'outer', but
167 // we don't want to do this check twice in one call in the normal case:
168 // once in QueryInterface and once in DelegatedQueryInterface.
169 if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
170 NS_ADDREF(this);
171 *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
172 return NS_OK;
173 }
175 return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
176 }
178 NS_IMETHODIMP
179 nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
180 {
181 if (nullptr == aInstancePtr) {
182 NS_PRECONDITION(0, "null pointer");
183 return NS_ERROR_NULL_POINTER;
184 }
186 if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
187 *aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS);
188 return NS_OK;
189 }
191 if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) {
192 *aInstancePtr =
193 NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
194 return NS_OK;
195 }
197 if (!IsValid())
198 return NS_ERROR_UNEXPECTED;
200 // Always check for this first so that our 'outer' can get this interface
201 // from us without recurring into a call to the outer's QI!
202 if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
203 NS_ADDREF(this);
204 *aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
205 return NS_OK;
206 }
208 nsISupports* outer = GetAggregatedNativeObject();
209 if (outer)
210 return outer->QueryInterface(aIID, aInstancePtr);
212 // else...
214 return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
215 }
218 // For a description of nsXPCWrappedJS lifetime and reference counting, see
219 // the comment at the top of this file.
221 MozExternalRefCountType
222 nsXPCWrappedJS::AddRef(void)
223 {
224 if (!MOZ_LIKELY(NS_IsMainThread()))
225 MOZ_CRASH();
227 MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
228 nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
229 nsrefcnt cnt = mRefCnt.incr(base);
230 NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
232 if (2 == cnt && IsValid()) {
233 GetJSObject(); // Unmark gray JSObject.
234 mClass->GetRuntime()->AddWrappedJSRoot(this);
235 }
237 return cnt;
238 }
240 MozExternalRefCountType
241 nsXPCWrappedJS::Release(void)
242 {
243 if (!MOZ_LIKELY(NS_IsMainThread()))
244 MOZ_CRASH();
245 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
246 NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS);
248 bool shouldDelete = false;
249 nsISupports *base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
250 nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete);
251 NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
253 if (0 == cnt) {
254 if (MOZ_UNLIKELY(shouldDelete)) {
255 mRefCnt.stabilizeForDeletion();
256 DeleteCycleCollectable();
257 } else {
258 mRefCnt.incr(base);
259 Destroy();
260 mRefCnt.decr(base);
261 }
262 } else if (1 == cnt) {
263 if (IsValid())
264 RemoveFromRootSet();
266 // If we are not a root wrapper being used from a weak reference,
267 // then the extra ref is not needed and we can let outselves be
268 // deleted.
269 if (!HasWeakReferences())
270 return Release();
272 MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references");
273 }
274 return cnt;
275 }
277 NS_IMETHODIMP_(void)
278 nsXPCWrappedJS::DeleteCycleCollectable(void)
279 {
280 delete this;
281 }
283 void
284 nsXPCWrappedJS::TraceJS(JSTracer* trc)
285 {
286 MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
287 trc->setTracingDetails(GetTraceName, this, 0);
288 JS_CallHeapObjectTracer(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
289 }
291 // static
292 void
293 nsXPCWrappedJS::GetTraceName(JSTracer* trc, char *buf, size_t bufsize)
294 {
295 const nsXPCWrappedJS* self = static_cast<const nsXPCWrappedJS*>
296 (trc->debugPrintArg());
297 JS_snprintf(buf, bufsize, "nsXPCWrappedJS[%s,0x%p:0x%p].mJSObj",
298 self->GetClass()->GetInterfaceName(), self, self->mXPTCStub);
299 }
301 NS_IMETHODIMP
302 nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
303 {
304 if (!IsRootWrapper())
305 return mRoot->GetWeakReference(aInstancePtr);
307 return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
308 }
310 JSObject*
311 nsXPCWrappedJS::GetJSObject()
312 {
313 if (mJSObj) {
314 JS::ExposeObjectToActiveJS(mJSObj);
315 }
316 return mJSObj;
317 }
319 // static
320 nsresult
321 nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj,
322 REFNSIID aIID,
323 nsXPCWrappedJS** wrapperResult)
324 {
325 // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
326 if (!MOZ_LIKELY(NS_IsMainThread()))
327 MOZ_CRASH();
329 AutoJSContext cx;
330 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
331 JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
332 if (!map) {
333 MOZ_ASSERT(map,"bad map");
334 return NS_ERROR_FAILURE;
335 }
337 nsRefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID);
338 if (!clasp)
339 return NS_ERROR_FAILURE;
341 JS::RootedObject rootJSObj(cx, clasp->GetRootJSObject(cx, jsObj));
342 if (!rootJSObj)
343 return NS_ERROR_FAILURE;
345 nsRefPtr<nsXPCWrappedJS> root = map->Find(rootJSObj);
346 if (root) {
347 nsRefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID);
348 if (wrapper) {
349 wrapper.forget(wrapperResult);
350 return NS_OK;
351 }
352 } else if (rootJSObj != jsObj) {
354 // Make a new root wrapper, because there is no existing
355 // root wrapper, and the wrapper we are trying to make isn't
356 // a root.
357 nsRefPtr<nsXPCWrappedJSClass> rootClasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports));
358 if (!rootClasp)
359 return NS_ERROR_FAILURE;
361 root = new nsXPCWrappedJS(cx, rootJSObj, rootClasp, nullptr);
362 }
364 nsRefPtr<nsXPCWrappedJS> wrapper = new nsXPCWrappedJS(cx, jsObj, clasp, root);
365 wrapper.forget(wrapperResult);
366 return NS_OK;
367 }
369 nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx,
370 JSObject* aJSObj,
371 nsXPCWrappedJSClass* aClass,
372 nsXPCWrappedJS* root)
373 : mJSObj(aJSObj),
374 mClass(aClass),
375 mRoot(root ? root : MOZ_THIS_IN_INITIALIZER_LIST()),
376 mNext(nullptr)
377 {
378 InitStub(GetClass()->GetIID());
380 // There is an extra AddRef to support weak references to wrappers
381 // that are subject to finalization. See the top of the file for more
382 // details.
383 NS_ADDREF_THIS();
385 if (IsRootWrapper()) {
386 nsXPConnect::GetRuntimeInstance()->GetWrappedJSMap()->Add(cx, this);
387 } else {
388 NS_ADDREF(mRoot);
389 mNext = mRoot->mNext;
390 mRoot->mNext = this;
391 }
392 }
394 nsXPCWrappedJS::~nsXPCWrappedJS()
395 {
396 Destroy();
397 }
399 void
400 nsXPCWrappedJS::Destroy()
401 {
402 MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion");
404 if (IsRootWrapper()) {
405 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
406 JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
407 if (map)
408 map->Remove(this);
409 }
410 Unlink();
411 }
413 void
414 nsXPCWrappedJS::Unlink()
415 {
416 if (IsValid()) {
417 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
418 if (rt) {
419 if (IsRootWrapper()) {
420 JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
421 if (map)
422 map->Remove(this);
423 }
425 if (mRefCnt > 1)
426 RemoveFromRootSet();
427 }
429 mJSObj = nullptr;
430 }
432 if (IsRootWrapper()) {
433 ClearWeakReferences();
434 } else if (mRoot) {
435 // unlink this wrapper
436 nsXPCWrappedJS* cur = mRoot;
437 while (1) {
438 if (cur->mNext == this) {
439 cur->mNext = mNext;
440 break;
441 }
442 cur = cur->mNext;
443 MOZ_ASSERT(cur, "failed to find wrapper in its own chain");
444 }
445 // let the root go
446 NS_RELEASE(mRoot);
447 }
449 mClass = nullptr;
450 if (mOuter) {
451 XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
452 if (rt->GCIsRunning()) {
453 nsContentUtils::DeferredFinalize(mOuter.forget().take());
454 } else {
455 mOuter = nullptr;
456 }
457 }
458 }
460 nsXPCWrappedJS*
461 nsXPCWrappedJS::Find(REFNSIID aIID)
462 {
463 if (aIID.Equals(NS_GET_IID(nsISupports)))
464 return mRoot;
466 for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
467 if (aIID.Equals(cur->GetIID()))
468 return cur;
469 }
471 return nullptr;
472 }
474 // check if asking for an interface that some wrapper in the chain inherits from
475 nsXPCWrappedJS*
476 nsXPCWrappedJS::FindInherited(REFNSIID aIID)
477 {
478 MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence");
480 for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
481 bool found;
482 if (NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()->
483 HasAncestor(&aIID, &found)) && found)
484 return cur;
485 }
487 return nullptr;
488 }
490 NS_IMETHODIMP
491 nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** infoResult)
492 {
493 MOZ_ASSERT(GetClass(), "wrapper without class");
494 MOZ_ASSERT(GetClass()->GetInterfaceInfo(), "wrapper class without interface");
496 // Since failing to get this info will crash some platforms(!), we keep
497 // mClass valid at shutdown time.
499 nsCOMPtr<nsIInterfaceInfo> info = GetClass()->GetInterfaceInfo();
500 if (!info)
501 return NS_ERROR_UNEXPECTED;
502 info.forget(infoResult);
503 return NS_OK;
504 }
506 NS_IMETHODIMP
507 nsXPCWrappedJS::CallMethod(uint16_t methodIndex,
508 const XPTMethodDescriptor* info,
509 nsXPTCMiniVariant* params)
510 {
511 // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
512 if (!MOZ_LIKELY(NS_IsMainThread()))
513 MOZ_CRASH();
515 if (!IsValid())
516 return NS_ERROR_UNEXPECTED;
517 return GetClass()->CallMethod(this, methodIndex, info, params);
518 }
520 NS_IMETHODIMP
521 nsXPCWrappedJS::GetInterfaceIID(nsIID** iid)
522 {
523 NS_PRECONDITION(iid, "bad param");
525 *iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID));
526 return *iid ? NS_OK : NS_ERROR_UNEXPECTED;
527 }
529 void
530 nsXPCWrappedJS::SystemIsBeingShutDown()
531 {
532 // XXX It turns out that it is better to leak here then to do any Releases
533 // and have them propagate into all sorts of mischief as the system is being
534 // shutdown. This was learned the hard way :(
536 // mJSObj == nullptr is used to indicate that the wrapper is no longer valid
537 // and that calls should fail without trying to use any of the
538 // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
540 // NOTE: that mClass is retained so that GetInterfaceInfo can continue to
541 // work (and avoid crashing some platforms).
543 // Use of unsafeGet() is to avoid triggering post barriers in shutdown, as
544 // this will access the chunk containing mJSObj, which may have been freed
545 // at this point.
546 *mJSObj.unsafeGet() = nullptr;
548 // Notify other wrappers in the chain.
549 if (mNext)
550 mNext->SystemIsBeingShutDown();
551 }
553 /***************************************************************************/
555 /* readonly attribute nsISimpleEnumerator enumerator; */
556 NS_IMETHODIMP
557 nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate)
558 {
559 AutoJSContext cx;
560 XPCCallContext ccx(NATIVE_CALLER, cx);
561 if (!ccx.IsValid())
562 return NS_ERROR_UNEXPECTED;
564 return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(),
565 aEnumerate);
566 }
568 /* nsIVariant getProperty (in AString name); */
569 NS_IMETHODIMP
570 nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant **_retval)
571 {
572 AutoJSContext cx;
573 XPCCallContext ccx(NATIVE_CALLER, cx);
574 if (!ccx.IsValid())
575 return NS_ERROR_UNEXPECTED;
577 return nsXPCWrappedJSClass::
578 GetNamedPropertyAsVariant(ccx, GetJSObject(), name, _retval);
579 }
581 /***************************************************************************/
583 NS_IMETHODIMP
584 nsXPCWrappedJS::DebugDump(int16_t depth)
585 {
586 #ifdef DEBUG
587 XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get()));
588 XPC_LOG_INDENT();
590 XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \
591 IsRootWrapper() ? "ROOT":"non-root", mJSObj.get()));
592 char* name;
593 GetClass()->GetInterfaceInfo()->GetName(&name);
594 XPC_LOG_ALWAYS(("interface name is %s", name));
595 if (name)
596 nsMemory::Free(name);
597 char * iid = GetClass()->GetIID().ToString();
598 XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
599 if (iid)
600 NS_Free(iid);
601 XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x", mClass.get()));
603 if (!IsRootWrapper())
604 XPC_LOG_OUTDENT();
605 if (mNext) {
606 if (IsRootWrapper()) {
607 XPC_LOG_ALWAYS(("Additional wrappers for this object..."));
608 XPC_LOG_INDENT();
609 }
610 mNext->DebugDump(depth);
611 if (IsRootWrapper())
612 XPC_LOG_OUTDENT();
613 }
614 if (IsRootWrapper())
615 XPC_LOG_OUTDENT();
616 #endif
617 return NS_OK;
618 }