/src/mozilla-central/dom/xbl/nsBindingManager.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsBindingManager.h" |
8 | | |
9 | | #include "nsAutoPtr.h" |
10 | | #include "nsCOMPtr.h" |
11 | | #include "nsXBLService.h" |
12 | | #include "nsIInputStream.h" |
13 | | #include "nsIURI.h" |
14 | | #include "nsIURL.h" |
15 | | #include "nsIChannel.h" |
16 | | #include "nsString.h" |
17 | | #include "plstr.h" |
18 | | #include "nsIContent.h" |
19 | | #include "nsIContentInlines.h" |
20 | | #include "nsIDocument.h" |
21 | | #include "nsContentUtils.h" |
22 | | #include "nsIPresShell.h" |
23 | | #include "nsIPresShellInlines.h" |
24 | | #include "nsIXMLContentSink.h" |
25 | | #include "nsContentCID.h" |
26 | | #include "mozilla/dom/XMLDocument.h" |
27 | | #include "nsIStreamListener.h" |
28 | | #include "ChildIterator.h" |
29 | | #include "nsITimer.h" |
30 | | |
31 | | #include "nsXBLBinding.h" |
32 | | #include "nsXBLPrototypeBinding.h" |
33 | | #include "nsXBLDocumentInfo.h" |
34 | | #include "mozilla/dom/XBLChildrenElement.h" |
35 | | #ifdef MOZ_XUL |
36 | | #include "nsXULPrototypeCache.h" |
37 | | #endif |
38 | | |
39 | | #include "nsIWeakReference.h" |
40 | | |
41 | | #include "nsWrapperCacheInlines.h" |
42 | | #include "nsIXPConnect.h" |
43 | | #include "nsDOMCID.h" |
44 | | #include "nsIScriptGlobalObject.h" |
45 | | #include "nsTHashtable.h" |
46 | | |
47 | | #include "nsIScriptContext.h" |
48 | | #include "xpcpublic.h" |
49 | | #include "js/Wrapper.h" |
50 | | |
51 | | #include "nsThreadUtils.h" |
52 | | #include "mozilla/dom/NodeListBinding.h" |
53 | | #include "mozilla/dom/ScriptSettings.h" |
54 | | #include "mozilla/Unused.h" |
55 | | |
56 | | using namespace mozilla; |
57 | | using namespace mozilla::dom; |
58 | | |
59 | | // Implement our nsISupports methods |
60 | | |
61 | | NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager) |
62 | | |
63 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager) |
64 | 0 | tmp->mDestroyed = true; |
65 | 0 |
|
66 | 0 | if (tmp->mBoundContentSet) |
67 | 0 | tmp->mBoundContentSet->Clear(); |
68 | 0 |
|
69 | 0 | if (tmp->mDocumentTable) |
70 | 0 | tmp->mDocumentTable->Clear(); |
71 | 0 |
|
72 | 0 | if (tmp->mLoadingDocTable) |
73 | 0 | tmp->mLoadingDocTable->Clear(); |
74 | 0 |
|
75 | 0 | if (tmp->mWrapperTable) { |
76 | 0 | tmp->mWrapperTable->Clear(); |
77 | 0 | tmp->mWrapperTable = nullptr; |
78 | 0 | } |
79 | 0 |
|
80 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttachedStack) |
81 | 0 |
|
82 | 0 | if (tmp->mProcessAttachedQueueEvent) { |
83 | 0 | tmp->mProcessAttachedQueueEvent->Revoke(); |
84 | 0 | } |
85 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
86 | | |
87 | | |
88 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager) |
89 | 0 | // The hashes keyed on nsIContent are traversed from the nsIContent itself. |
90 | 0 | if (tmp->mDocumentTable) { |
91 | 0 | for (auto iter = tmp->mDocumentTable->Iter(); !iter.Done(); iter.Next()) { |
92 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocumentTable value"); |
93 | 0 | cb.NoteXPCOMChild(iter.UserData()); |
94 | 0 | } |
95 | 0 | } |
96 | 0 | if (tmp->mLoadingDocTable) { |
97 | 0 | for (auto iter = tmp->mLoadingDocTable->Iter(); !iter.Done(); iter.Next()) { |
98 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mLoadingDocTable value"); |
99 | 0 | cb.NoteXPCOMChild(iter.UserData()); |
100 | 0 | } |
101 | 0 | } |
102 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttachedStack) |
103 | 0 | // No need to traverse mProcessAttachedQueueEvent, since it'll just |
104 | 0 | // fire at some point or become revoke and drop its ref to us. |
105 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
106 | | |
107 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager) |
108 | 0 | NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) |
109 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
110 | 0 | NS_INTERFACE_MAP_END |
111 | | |
112 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager) |
113 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager) |
114 | | |
115 | | // Constructors/Destructors |
116 | | nsBindingManager::nsBindingManager(nsIDocument* aDocument) |
117 | | : mProcessingAttachedStack(false), |
118 | | mDestroyed(false), |
119 | | mAttachedStackSizeOnOutermost(0), |
120 | | mDocument(aDocument) |
121 | 0 | { |
122 | 0 | } |
123 | | |
124 | | nsBindingManager::~nsBindingManager(void) |
125 | 0 | { |
126 | 0 | mDestroyed = true; |
127 | 0 | } |
128 | | |
129 | | nsXBLBinding* |
130 | | nsBindingManager::GetBindingWithContent(const nsIContent* aContent) |
131 | 0 | { |
132 | 0 | nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr; |
133 | 0 | return binding ? binding->GetBindingWithContent() : nullptr; |
134 | 0 | } |
135 | | |
136 | | void |
137 | | nsBindingManager::AddBoundContent(nsIContent* aContent) |
138 | 0 | { |
139 | 0 | if (!mBoundContentSet) { |
140 | 0 | mBoundContentSet = new nsTHashtable<nsRefPtrHashKey<nsIContent> >; |
141 | 0 | } |
142 | 0 | mBoundContentSet->PutEntry(aContent); |
143 | 0 | } |
144 | | |
145 | | void |
146 | | nsBindingManager::RemoveBoundContent(nsIContent* aContent) |
147 | 0 | { |
148 | 0 | if (mBoundContentSet) { |
149 | 0 | mBoundContentSet->RemoveEntry(aContent); |
150 | 0 | } |
151 | 0 |
|
152 | 0 | // The death of the bindings means the death of the JS wrapper. |
153 | 0 | SetWrappedJS(aContent, nullptr); |
154 | 0 | } |
155 | | |
156 | | nsIXPConnectWrappedJS* |
157 | | nsBindingManager::GetWrappedJS(nsIContent* aContent) |
158 | 0 | { |
159 | 0 | if (!mWrapperTable) { |
160 | 0 | return nullptr; |
161 | 0 | } |
162 | 0 | |
163 | 0 | if (!aContent || !aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { |
164 | 0 | return nullptr; |
165 | 0 | } |
166 | 0 | |
167 | 0 | return mWrapperTable->GetWeak(aContent); |
168 | 0 | } |
169 | | |
170 | | nsresult |
171 | | nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS) |
172 | 0 | { |
173 | 0 | if (mDestroyed) { |
174 | 0 | return NS_OK; |
175 | 0 | } |
176 | 0 | |
177 | 0 | if (aWrappedJS) { |
178 | 0 | // lazily create the table, but only when adding elements |
179 | 0 | if (!mWrapperTable) { |
180 | 0 | mWrapperTable = new WrapperHashtable(); |
181 | 0 | } |
182 | 0 | aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR); |
183 | 0 |
|
184 | 0 | NS_ASSERTION(aContent, "key must be non-null"); |
185 | 0 | if (!aContent) return NS_ERROR_INVALID_ARG; |
186 | 0 | |
187 | 0 | mWrapperTable->Put(aContent, aWrappedJS); |
188 | 0 |
|
189 | 0 | return NS_OK; |
190 | 0 | } |
191 | 0 | |
192 | 0 | // no value, so remove the key from the table |
193 | 0 | if (mWrapperTable) { |
194 | 0 | mWrapperTable->Remove(aContent); |
195 | 0 | } |
196 | 0 |
|
197 | 0 | return NS_OK; |
198 | 0 | } |
199 | | |
200 | | void |
201 | | nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent, |
202 | | nsIDocument* aOldDocument, |
203 | | DestructorHandling aDestructorHandling) |
204 | 0 | { |
205 | 0 | MOZ_ASSERT(aOldDocument != nullptr, "no old document"); |
206 | 0 |
|
207 | 0 | RefPtr<nsXBLBinding> binding = aContent->GetXBLBinding(); |
208 | 0 | if (binding) { |
209 | 0 | // The binding manager may have been destroyed before a runnable |
210 | 0 | // has had a chance to reach this point. If so, we bail out on calling |
211 | 0 | // BindingDetached (which may invoke a XBL destructor) and |
212 | 0 | // ChangeDocument, but we still want to clear out the binding |
213 | 0 | // and insertion parent that may hold references. |
214 | 0 | if (!mDestroyed && aDestructorHandling == eRunDtor) { |
215 | 0 | binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement()); |
216 | 0 | binding->ChangeDocument(aOldDocument, nullptr); |
217 | 0 | } |
218 | 0 |
|
219 | 0 | aContent->AsElement()->SetXBLBinding(nullptr, this); |
220 | 0 | } |
221 | 0 |
|
222 | 0 | // Clear out insertion point and content lists. |
223 | 0 | aContent->SetXBLInsertionPoint(nullptr); |
224 | 0 | } |
225 | | |
226 | | nsAtom* |
227 | | nsBindingManager::ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID) |
228 | 0 | { |
229 | 0 | nsXBLBinding *binding = aContent->GetXBLBinding(); |
230 | 0 |
|
231 | 0 | if (binding) { |
232 | 0 | nsAtom* base = binding->GetBaseTag(aNameSpaceID); |
233 | 0 |
|
234 | 0 | if (base) { |
235 | 0 | return base; |
236 | 0 | } |
237 | 0 | } |
238 | 0 | |
239 | 0 | *aNameSpaceID = aContent->GetNameSpaceID(); |
240 | 0 | return aContent->NodeInfo()->NameAtom(); |
241 | 0 | } |
242 | | |
243 | | nsINodeList* |
244 | | nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent) |
245 | 0 | { |
246 | 0 | nsXBLBinding* binding = GetBindingWithContent(aContent); |
247 | 0 | return binding ? binding->GetAnonymousNodeList() : nullptr; |
248 | 0 | } |
249 | | |
250 | | nsresult |
251 | | nsBindingManager::ClearBinding(Element* aElement) |
252 | 0 | { |
253 | 0 | // Hold a ref to the binding so it won't die when we remove it from our table |
254 | 0 | RefPtr<nsXBLBinding> binding = |
255 | 0 | aElement ? aElement->GetXBLBinding() : nullptr; |
256 | 0 |
|
257 | 0 | if (!binding) { |
258 | 0 | return NS_OK; |
259 | 0 | } |
260 | 0 | |
261 | 0 | // For now we can only handle removing a binding if it's the only one |
262 | 0 | NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE); |
263 | 0 |
|
264 | 0 | // Hold strong ref in case removing the binding tries to close the |
265 | 0 | // window or something. |
266 | 0 | // XXXbz should that be ownerdoc? Wouldn't we need a ref to the |
267 | 0 | // currentdoc too? What's the one that should be passed to |
268 | 0 | // ChangeDocument? |
269 | 0 | nsCOMPtr<nsIDocument> doc = aElement->OwnerDoc(); |
270 | 0 |
|
271 | 0 | // Destroy the frames here before the UnbindFromTree happens. |
272 | 0 | nsIPresShell* presShell = doc->GetShell(); |
273 | 0 | if (presShell) { |
274 | 0 | presShell->DestroyFramesForAndRestyle(aElement); |
275 | 0 | } |
276 | 0 |
|
277 | 0 | // Finally remove the binding... |
278 | 0 | // XXXbz this doesn't remove the implementation! Should fix! Until |
279 | 0 | // then we need the explicit UnhookEventHandlers here. |
280 | 0 | binding->UnhookEventHandlers(); |
281 | 0 | binding->ChangeDocument(doc, nullptr); |
282 | 0 | aElement->SetXBLBinding(nullptr, this); |
283 | 0 | binding->MarkForDeath(); |
284 | 0 |
|
285 | 0 | // ...and recreate its frames. We need to do this since the frames may have |
286 | 0 | // been removed and style may have changed due to the removal of the |
287 | 0 | // anonymous children. |
288 | 0 | // XXXbz this should be using the current doc (if any), not the owner doc. |
289 | 0 | presShell = doc->GetShell(); // get the shell again, just in case it changed |
290 | 0 | NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); |
291 | 0 |
|
292 | 0 | presShell->PostRecreateFramesFor(aElement); |
293 | 0 | return NS_OK; |
294 | 0 | } |
295 | | |
296 | | nsresult |
297 | | nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc, |
298 | | nsIURI* aURL, |
299 | | nsIPrincipal* aOriginPrincipal) |
300 | 0 | { |
301 | 0 | MOZ_ASSERT(aURL, "Must have a URI to load!"); |
302 | 0 |
|
303 | 0 | // First we need to load our binding. |
304 | 0 | nsXBLService* xblService = nsXBLService::GetInstance(); |
305 | 0 | if (!xblService) |
306 | 0 | return NS_ERROR_FAILURE; |
307 | 0 | |
308 | 0 | // Load the binding doc. |
309 | 0 | RefPtr<nsXBLDocumentInfo> info; |
310 | 0 | xblService->LoadBindingDocumentInfo(nullptr, aBoundDoc, aURL, |
311 | 0 | aOriginPrincipal, true, |
312 | 0 | getter_AddRefs(info)); |
313 | 0 | if (!info) |
314 | 0 | return NS_ERROR_FAILURE; |
315 | 0 | |
316 | 0 | return NS_OK; |
317 | 0 | } |
318 | | |
319 | | void |
320 | | nsBindingManager::RemoveFromAttachedQueue(nsXBLBinding* aBinding) |
321 | 0 | { |
322 | 0 | // Don't remove items here as that could mess up an executing |
323 | 0 | // ProcessAttachedQueue. Instead, null the entry in the queue. |
324 | 0 | size_t index = mAttachedStack.IndexOf(aBinding); |
325 | 0 | if (index != nsBindingList::NoIndex) { |
326 | 0 | mAttachedStack[index] = nullptr; |
327 | 0 | } |
328 | 0 | } |
329 | | |
330 | | nsresult |
331 | | nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding) |
332 | 0 | { |
333 | 0 | mAttachedStack.AppendElement(aBinding); |
334 | 0 |
|
335 | 0 | // If we're in the middle of processing our queue already, don't |
336 | 0 | // bother posting the event. |
337 | 0 | if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) { |
338 | 0 | PostProcessAttachedQueueEvent(); |
339 | 0 | } |
340 | 0 |
|
341 | 0 | // Make sure that flushes will flush out the new items as needed. |
342 | 0 | if (nsIPresShell* shell = mDocument->GetShell()) { |
343 | 0 | shell->SetNeedStyleFlush(); |
344 | 0 | } |
345 | 0 |
|
346 | 0 | return NS_OK; |
347 | 0 |
|
348 | 0 | } |
349 | | |
350 | | void |
351 | | nsBindingManager::PostProcessAttachedQueueEvent() |
352 | 0 | { |
353 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
354 | 0 | if (!mDocument) { |
355 | 0 | return; |
356 | 0 | } |
357 | 0 | mProcessAttachedQueueEvent = |
358 | 0 | NewRunnableMethod("nsBindingManager::DoProcessAttachedQueue", |
359 | 0 | this, &nsBindingManager::DoProcessAttachedQueue); |
360 | 0 | nsresult rv = mDocument->EventTargetFor(TaskCategory::Other)->Dispatch(do_AddRef(mProcessAttachedQueueEvent)); |
361 | 0 | if (NS_SUCCEEDED(rv)) { |
362 | 0 | mDocument->BlockOnload(); |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | // static |
367 | | void |
368 | | nsBindingManager::PostPAQEventCallback(nsITimer* aTimer, void* aClosure) |
369 | 0 | { |
370 | 0 | RefPtr<nsBindingManager> mgr = |
371 | 0 | already_AddRefed<nsBindingManager>(static_cast<nsBindingManager*>(aClosure)); |
372 | 0 | mgr->PostProcessAttachedQueueEvent(); |
373 | 0 | NS_RELEASE(aTimer); |
374 | 0 | } |
375 | | |
376 | | void |
377 | | nsBindingManager::DoProcessAttachedQueue() |
378 | 0 | { |
379 | 0 | if (!mProcessingAttachedStack) { |
380 | 0 | ProcessAttachedQueue(); |
381 | 0 |
|
382 | 0 | NS_ASSERTION(mAttachedStack.Length() == 0, |
383 | 0 | "Shouldn't have pending bindings!"); |
384 | 0 |
|
385 | 0 | mProcessAttachedQueueEvent = nullptr; |
386 | 0 | } else { |
387 | 0 | // Someone's doing event processing from inside a constructor. |
388 | 0 | // They're evil, but we'll fight back! Just poll on them being |
389 | 0 | // done and repost the attached queue event. |
390 | 0 | // |
391 | 0 | // But don't poll in a tight loop -- otherwise we keep the Gecko |
392 | 0 | // event loop non-empty and trigger bug 1021240 on OS X. |
393 | 0 | nsresult rv = NS_ERROR_FAILURE; |
394 | 0 | nsCOMPtr<nsITimer> timer; |
395 | 0 | rv = NS_NewTimerWithFuncCallback(getter_AddRefs(timer), |
396 | 0 | PostPAQEventCallback, |
397 | 0 | this, |
398 | 0 | 100, |
399 | 0 | nsITimer::TYPE_ONE_SHOT, |
400 | 0 | "nsBindingManager::DoProcessAttachedQueue"); |
401 | 0 | if (NS_SUCCEEDED(rv)) { |
402 | 0 | NS_ADDREF_THIS(); |
403 | 0 | // We drop our reference to the timer here, since the timer callback is |
404 | 0 | // responsible for releasing the object. |
405 | 0 | Unused << timer.forget().take(); |
406 | 0 | } |
407 | 0 | } |
408 | 0 |
|
409 | 0 | // No matter what, unblock onload for the event that's fired. |
410 | 0 | if (mDocument) { |
411 | 0 | // Hold a strong reference while calling UnblockOnload since that might |
412 | 0 | // run script. |
413 | 0 | nsCOMPtr<nsIDocument> doc = mDocument; |
414 | 0 | doc->UnblockOnload(true); |
415 | 0 | } |
416 | 0 | } |
417 | | |
418 | | void |
419 | | nsBindingManager::ProcessAttachedQueueInternal(uint32_t aSkipSize) |
420 | 0 | { |
421 | 0 | mProcessingAttachedStack = true; |
422 | 0 |
|
423 | 0 | // Excute constructors. Do this from high index to low |
424 | 0 | while (mAttachedStack.Length() > aSkipSize) { |
425 | 0 | uint32_t lastItem = mAttachedStack.Length() - 1; |
426 | 0 | RefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem); |
427 | 0 | mAttachedStack.RemoveElementAt(lastItem); |
428 | 0 | if (binding) { |
429 | 0 | binding->ExecuteAttachedHandler(); |
430 | 0 | } |
431 | 0 | } |
432 | 0 |
|
433 | 0 | // If NodeWillBeDestroyed has run we don't want to clobber |
434 | 0 | // mProcessingAttachedStack set there. |
435 | 0 | if (mDocument) { |
436 | 0 | mProcessingAttachedStack = false; |
437 | 0 | } |
438 | 0 |
|
439 | 0 | NS_ASSERTION(mAttachedStack.Length() == aSkipSize, "How did we get here?"); |
440 | 0 |
|
441 | 0 | mAttachedStack.Compact(); |
442 | 0 | } |
443 | | |
444 | | // Keep bindings and bound elements alive while executing detached handlers. |
445 | | void |
446 | | nsBindingManager::ExecuteDetachedHandlers() |
447 | 0 | { |
448 | 0 | // Walk our hashtable of bindings. |
449 | 0 | if (!mBoundContentSet) { |
450 | 0 | return; |
451 | 0 | } |
452 | 0 | |
453 | 0 | nsCOMArray<nsIContent> boundElements; |
454 | 0 | nsBindingList bindings; |
455 | 0 |
|
456 | 0 | for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) { |
457 | 0 | nsXBLBinding* binding = iter.Get()->GetKey()->GetXBLBinding(); |
458 | 0 | if (binding && bindings.AppendElement(binding)) { |
459 | 0 | if (!boundElements.AppendObject(binding->GetBoundElement())) { |
460 | 0 | bindings.RemoveLastElement(); |
461 | 0 | } |
462 | 0 | } |
463 | 0 | } |
464 | 0 |
|
465 | 0 | uint32_t i, count = bindings.Length(); |
466 | 0 | for (i = 0; i < count; ++i) { |
467 | 0 | bindings[i]->ExecuteDetachedHandler(); |
468 | 0 | } |
469 | 0 | } |
470 | | |
471 | | nsresult |
472 | | nsBindingManager::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo) |
473 | 0 | { |
474 | 0 | MOZ_ASSERT(aDocumentInfo, "Must have a non-null documentinfo!"); |
475 | 0 |
|
476 | 0 | if (!mDocumentTable) { |
477 | 0 | mDocumentTable = new nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>(); |
478 | 0 | } |
479 | 0 |
|
480 | 0 | mDocumentTable->Put(aDocumentInfo->DocumentURI(), aDocumentInfo); |
481 | 0 |
|
482 | 0 | return NS_OK; |
483 | 0 | } |
484 | | |
485 | | void |
486 | | nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo) |
487 | 0 | { |
488 | 0 | if (mDocumentTable) { |
489 | 0 | mDocumentTable->Remove(aDocumentInfo->DocumentURI()); |
490 | 0 | } |
491 | 0 | } |
492 | | |
493 | | nsXBLDocumentInfo* |
494 | | nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL) |
495 | 0 | { |
496 | 0 | if (!mDocumentTable) |
497 | 0 | return nullptr; |
498 | 0 | |
499 | 0 | return mDocumentTable->GetWeak(aURL); |
500 | 0 | } |
501 | | |
502 | | nsresult |
503 | | nsBindingManager::PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener) |
504 | 0 | { |
505 | 0 | MOZ_ASSERT(aListener, "Must have a non-null listener!"); |
506 | 0 |
|
507 | 0 | if (!mLoadingDocTable) { |
508 | 0 | mLoadingDocTable = |
509 | 0 | new nsInterfaceHashtable<nsURIHashKey,nsIStreamListener>(); |
510 | 0 | } |
511 | 0 | mLoadingDocTable->Put(aURL, aListener); |
512 | 0 |
|
513 | 0 | return NS_OK; |
514 | 0 | } |
515 | | |
516 | | nsIStreamListener* |
517 | | nsBindingManager::GetLoadingDocListener(nsIURI* aURL) |
518 | 0 | { |
519 | 0 | if (!mLoadingDocTable) |
520 | 0 | return nullptr; |
521 | 0 | |
522 | 0 | return mLoadingDocTable->GetWeak(aURL); |
523 | 0 | } |
524 | | |
525 | | void |
526 | | nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL) |
527 | 0 | { |
528 | 0 | if (mLoadingDocTable) { |
529 | 0 | mLoadingDocTable->Remove(aURL); |
530 | 0 | } |
531 | 0 | } |
532 | | |
533 | | void |
534 | | nsBindingManager::FlushSkinBindings() |
535 | 0 | { |
536 | 0 | if (!mBoundContentSet) { |
537 | 0 | return; |
538 | 0 | } |
539 | 0 | |
540 | 0 | for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) { |
541 | 0 | nsXBLBinding* binding = iter.Get()->GetKey()->GetXBLBinding(); |
542 | 0 |
|
543 | 0 | if (binding->MarkedForDeath()) { |
544 | 0 | continue; |
545 | 0 | } |
546 | 0 | |
547 | 0 | nsAutoCString path; |
548 | 0 | binding->PrototypeBinding()->DocURI()->GetPathQueryRef(path); |
549 | 0 |
|
550 | 0 | if (!strncmp(path.get(), "/skin", 5)) { |
551 | 0 | binding->MarkForDeath(); |
552 | 0 | } |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | | // Used below to protect from recurring in QI calls through XPConnect. |
557 | | struct AntiRecursionData { |
558 | | nsIContent* element; |
559 | | REFNSIID iid; |
560 | | AntiRecursionData* next; |
561 | | |
562 | | AntiRecursionData(nsIContent* aElement, |
563 | | REFNSIID aIID, |
564 | | AntiRecursionData* aNext) |
565 | 0 | : element(aElement), iid(aIID), next(aNext) {} |
566 | | }; |
567 | | |
568 | | nsresult |
569 | | nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID, |
570 | | void** aResult) |
571 | 0 | { |
572 | 0 | *aResult = nullptr; |
573 | 0 | nsXBLBinding *binding = aContent ? aContent->GetXBLBinding() : nullptr; |
574 | 0 | if (binding) { |
575 | 0 | // The binding should not be asked for nsISupports |
576 | 0 | NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports"); |
577 | 0 | if (binding->ImplementsInterface(aIID)) { |
578 | 0 | nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent); |
579 | 0 |
|
580 | 0 | if (wrappedJS) { |
581 | 0 | // Protect from recurring in QI calls through XPConnect. |
582 | 0 | // This can happen when a second binding is being resolved. |
583 | 0 | // At that point a wrappedJS exists, but it doesn't yet know about |
584 | 0 | // the iid we are asking for. So, without this protection, |
585 | 0 | // AggregatedQueryInterface would end up recurring back into itself |
586 | 0 | // through this code. |
587 | 0 | // |
588 | 0 | // With this protection, when we detect the recursion we return |
589 | 0 | // NS_NOINTERFACE in the inner call. The outer call will then fall |
590 | 0 | // through (see below) and build a new chained wrappedJS for the iid. |
591 | 0 | // |
592 | 0 | // We're careful to not assume that only one direct nesting can occur |
593 | 0 | // because there is a call into JS in the middle and we can't assume |
594 | 0 | // that this code won't be reached by some more complex nesting path. |
595 | 0 | // |
596 | 0 | // NOTE: We *assume* this is single threaded, so we can use a |
597 | 0 | // static linked list to do the check. |
598 | 0 |
|
599 | 0 | static AntiRecursionData* list = nullptr; |
600 | 0 |
|
601 | 0 | for (AntiRecursionData* p = list; p; p = p->next) { |
602 | 0 | if (p->element == aContent && p->iid.Equals(aIID)) { |
603 | 0 | *aResult = nullptr; |
604 | 0 | return NS_NOINTERFACE; |
605 | 0 | } |
606 | 0 | } |
607 | 0 |
|
608 | 0 | AntiRecursionData item(aContent, aIID, list); |
609 | 0 | list = &item; |
610 | 0 |
|
611 | 0 | nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult); |
612 | 0 |
|
613 | 0 | list = item.next; |
614 | 0 |
|
615 | 0 | if (*aResult) |
616 | 0 | return rv; |
617 | 0 | |
618 | 0 | // No result was found, so this must be another XBL interface. |
619 | 0 | // Fall through to create a new wrapper. |
620 | 0 | } |
621 | 0 | |
622 | 0 | // We have never made a wrapper for this implementation. |
623 | 0 | // Create an XPC wrapper for the script object and hand it back. |
624 | 0 | AutoJSAPI jsapi; |
625 | 0 | jsapi.Init(); |
626 | 0 | JSContext* cx = jsapi.cx(); |
627 | 0 |
|
628 | 0 | nsIXPConnect *xpConnect = nsContentUtils::XPConnect(); |
629 | 0 |
|
630 | 0 | JS::Rooted<JSObject*> jsobj(cx, aContent->GetWrapper()); |
631 | 0 | NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE); |
632 | 0 |
|
633 | 0 | // If we're using an XBL scope, we need to use the Xray view to the bound |
634 | 0 | // content in order to view the full array of methods defined in the |
635 | 0 | // binding, some of which may not be exposed on the prototype of |
636 | 0 | // untrusted content. We don't need to consider add-on scopes here |
637 | 0 | // because they're chrome-only and no Xrays are involved. |
638 | 0 | // |
639 | 0 | // If there's no separate XBL scope, or if the reflector itself lives in |
640 | 0 | // the XBL scope, we'll end up with the global of the reflector. |
641 | 0 | JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, jsobj)); |
642 | 0 | NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED); |
643 | 0 | JSAutoRealm ar(cx, xblScope); |
644 | 0 | bool ok = JS_WrapObject(cx, &jsobj); |
645 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
646 | 0 | MOZ_ASSERT_IF(js::IsWrapper(jsobj), xpc::IsXrayWrapper(jsobj)); |
647 | 0 |
|
648 | 0 | nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, cx, |
649 | 0 | jsobj, aIID, aResult); |
650 | 0 | if (NS_FAILED(rv)) |
651 | 0 | return rv; |
652 | 0 | |
653 | 0 | // We successfully created a wrapper. We will own this wrapper for as long as the binding remains |
654 | 0 | // alive. At the time the binding is cleared out of the bindingManager, we will remove the wrapper |
655 | 0 | // from the bindingManager as well. |
656 | 0 | nsISupports* supp = static_cast<nsISupports*>(*aResult); |
657 | 0 | wrappedJS = do_QueryInterface(supp); |
658 | 0 | SetWrappedJS(aContent, wrappedJS); |
659 | 0 |
|
660 | 0 | return rv; |
661 | 0 | } |
662 | 0 | } |
663 | 0 |
|
664 | 0 | *aResult = nullptr; |
665 | 0 | return NS_NOINTERFACE; |
666 | 0 | } |
667 | | |
668 | | |
669 | | bool |
670 | | nsBindingManager::EnumerateBoundContentProtoBindings( |
671 | | const BoundContentProtoBindingCallback& aCallback) const |
672 | 0 | { |
673 | 0 | if (!mBoundContentSet) { |
674 | 0 | return true; |
675 | 0 | } |
676 | 0 | |
677 | 0 | nsTHashtable<nsPtrHashKey<nsXBLPrototypeBinding>> bindings; |
678 | 0 | for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) { |
679 | 0 | nsIContent* boundContent = iter.Get()->GetKey(); |
680 | 0 | for (nsXBLBinding* binding = boundContent->GetXBLBinding(); |
681 | 0 | binding; |
682 | 0 | binding = binding->GetBaseBinding()) { |
683 | 0 | nsXBLPrototypeBinding* proto = binding->PrototypeBinding(); |
684 | 0 | // If we have already invoked the callback with a binding, we |
685 | 0 | // should have also invoked it for all its base bindings, so we |
686 | 0 | // don't need to continue this loop anymore. |
687 | 0 | if (!bindings.EnsureInserted(proto)) { |
688 | 0 | break; |
689 | 0 | } |
690 | 0 | if (!aCallback(proto)) { |
691 | 0 | return false; |
692 | 0 | } |
693 | 0 | } |
694 | 0 | } |
695 | 0 |
|
696 | 0 | return true; |
697 | 0 | } |
698 | | |
699 | | void |
700 | | nsBindingManager::AppendAllSheets(nsTArray<StyleSheet*>& aArray) |
701 | 0 | { |
702 | 0 | EnumerateBoundContentProtoBindings([&aArray](nsXBLPrototypeBinding* aProto) { |
703 | 0 | aProto->AppendStyleSheetsTo(aArray); |
704 | 0 | return true; |
705 | 0 | }); |
706 | 0 | } |
707 | | |
708 | | static void |
709 | | InsertAppendedContent(XBLChildrenElement* aPoint, |
710 | | nsIContent* aFirstNewContent) |
711 | 0 | { |
712 | 0 | int32_t insertionIndex; |
713 | 0 | if (nsIContent* prevSibling = aFirstNewContent->GetPreviousSibling()) { |
714 | 0 | // If we have a previous sibling, then it must already be in aPoint. Find |
715 | 0 | // it and insert after it. |
716 | 0 | insertionIndex = aPoint->IndexOfInsertedChild(prevSibling); |
717 | 0 | MOZ_ASSERT(insertionIndex != -1); |
718 | 0 |
|
719 | 0 | // Our insertion index is one after our previous sibling's index. |
720 | 0 | ++insertionIndex; |
721 | 0 | } else { |
722 | 0 | // Otherwise, we append. |
723 | 0 | // TODO This is wrong for nested insertion points. In that case, we need to |
724 | 0 | // keep track of the right index to insert into. |
725 | 0 | insertionIndex = aPoint->InsertedChildrenLength(); |
726 | 0 | } |
727 | 0 |
|
728 | 0 | // Do the inserting. |
729 | 0 | for (nsIContent* currentChild = aFirstNewContent; |
730 | 0 | currentChild; |
731 | 0 | currentChild = currentChild->GetNextSibling()) { |
732 | 0 | aPoint->InsertInsertedChildAt(currentChild, insertionIndex++); |
733 | 0 | } |
734 | 0 | } |
735 | | |
736 | | void |
737 | | nsBindingManager::ContentAppended(nsIContent* aFirstNewContent) |
738 | 0 | { |
739 | 0 | // Try to find insertion points for all the new kids. |
740 | 0 | XBLChildrenElement* point = nullptr; |
741 | 0 | nsIContent* container = aFirstNewContent->GetParent(); |
742 | 0 | nsIContent* parent = container; |
743 | 0 |
|
744 | 0 | // Handle appending of default content. |
745 | 0 | if (parent && parent->IsActiveChildrenElement()) { |
746 | 0 | XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent); |
747 | 0 | if (childrenEl->HasInsertedChildren()) { |
748 | 0 | // Appending default content that isn't being used. Ignore. |
749 | 0 | return; |
750 | 0 | } |
751 | 0 | |
752 | 0 | childrenEl->MaybeSetupDefaultContent(); |
753 | 0 | parent = childrenEl->GetParent(); |
754 | 0 | } |
755 | 0 |
|
756 | 0 | bool first = true; |
757 | 0 | do { |
758 | 0 | nsXBLBinding* binding = GetBindingWithContent(parent); |
759 | 0 | if (!binding) { |
760 | 0 | break; |
761 | 0 | } |
762 | 0 | |
763 | 0 | if (binding->HasFilteredInsertionPoints()) { |
764 | 0 | // There are filtered insertion points involved, handle each child |
765 | 0 | // separately. |
766 | 0 | // We could optimize this in the case when we've nested a few levels |
767 | 0 | // deep already, without hitting bindings that have filtered insertion |
768 | 0 | // points. |
769 | 0 | for (nsIContent* currentChild = aFirstNewContent; currentChild; |
770 | 0 | currentChild = currentChild->GetNextSibling()) { |
771 | 0 | HandleChildInsertion(container, currentChild, true); |
772 | 0 | } |
773 | 0 |
|
774 | 0 | return; |
775 | 0 | } |
776 | 0 |
|
777 | 0 | point = binding->GetDefaultInsertionPoint(); |
778 | 0 | if (!point) { |
779 | 0 | break; |
780 | 0 | } |
781 | 0 | |
782 | 0 | // Even though we're in ContentAppended, nested insertion points force us |
783 | 0 | // to deal with this append as an insertion except in the outermost |
784 | 0 | // binding. |
785 | 0 | if (first) { |
786 | 0 | first = false; |
787 | 0 | for (nsIContent* child = aFirstNewContent; child; |
788 | 0 | child = child->GetNextSibling()) { |
789 | 0 | point->AppendInsertedChild(child, true); |
790 | 0 | } |
791 | 0 | } else { |
792 | 0 | InsertAppendedContent(point, aFirstNewContent); |
793 | 0 | } |
794 | 0 |
|
795 | 0 | nsIContent* newParent = point->GetParent(); |
796 | 0 | if (newParent == parent) { |
797 | 0 | break; |
798 | 0 | } |
799 | 0 | parent = newParent; |
800 | 0 | } while (parent); |
801 | 0 | } |
802 | | |
803 | | void |
804 | | nsBindingManager::ContentInserted(nsIContent* aChild) |
805 | 0 | { |
806 | 0 | HandleChildInsertion(aChild->GetParent(), aChild, false); |
807 | 0 | } |
808 | | |
809 | | void |
810 | | nsBindingManager::ContentRemoved(nsIContent* aChild, |
811 | | nsIContent* aPreviousSibling) |
812 | 0 | { |
813 | 0 | aChild->SetXBLInsertionPoint(nullptr); |
814 | 0 |
|
815 | 0 | XBLChildrenElement* point = nullptr; |
816 | 0 | nsIContent* parent = aChild->GetParent(); |
817 | 0 |
|
818 | 0 | // Handle appending of default content. |
819 | 0 | if (parent && parent->IsActiveChildrenElement()) { |
820 | 0 | XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent); |
821 | 0 | if (childrenEl->HasInsertedChildren()) { |
822 | 0 | // Removing default content that isn't being used. Ignore. |
823 | 0 | return; |
824 | 0 | } |
825 | 0 | |
826 | 0 | parent = childrenEl->GetParent(); |
827 | 0 | } |
828 | 0 |
|
829 | 0 | do { |
830 | 0 | nsXBLBinding* binding = GetBindingWithContent(parent); |
831 | 0 | if (!binding) { |
832 | 0 | // If aChild is XBL content, it might have <xbl:children> elements |
833 | 0 | // somewhere under it. We need to inform those elements that they're no |
834 | 0 | // longer in the tree so they can tell their distributed children that |
835 | 0 | // they're no longer distributed under them. |
836 | 0 | // XXX This is wrong. We need to do far more work to update the parent |
837 | 0 | // binding's list of insertion points and to get the new insertion parent |
838 | 0 | // for the newly-distributed children correct. |
839 | 0 | if (aChild->GetBindingParent()) { |
840 | 0 | ClearInsertionPointsRecursively(aChild); |
841 | 0 | } |
842 | 0 | return; |
843 | 0 | } |
844 | 0 |
|
845 | 0 | point = binding->FindInsertionPointFor(aChild); |
846 | 0 | if (!point) { |
847 | 0 | break; |
848 | 0 | } |
849 | 0 | |
850 | 0 | point->RemoveInsertedChild(aChild); |
851 | 0 |
|
852 | 0 | nsIContent* newParent = point->GetParent(); |
853 | 0 | if (newParent == parent) { |
854 | 0 | break; |
855 | 0 | } |
856 | 0 | parent = newParent; |
857 | 0 | } while (parent); |
858 | 0 | } |
859 | | |
860 | | void |
861 | | nsBindingManager::ClearInsertionPointsRecursively(nsIContent* aContent) |
862 | 0 | { |
863 | 0 | if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) { |
864 | 0 | static_cast<XBLChildrenElement*>(aContent)->ClearInsertedChildren(); |
865 | 0 | } |
866 | 0 |
|
867 | 0 | for (nsIContent* child = aContent->GetFirstChild(); child; |
868 | 0 | child = child->GetNextSibling()) { |
869 | 0 | ClearInsertionPointsRecursively(child); |
870 | 0 | } |
871 | 0 | } |
872 | | |
873 | | void |
874 | | nsBindingManager::DropDocumentReference() |
875 | 0 | { |
876 | 0 | mDestroyed = true; |
877 | 0 |
|
878 | 0 | // Make sure to not run any more XBL constructors |
879 | 0 | mProcessingAttachedStack = true; |
880 | 0 | if (mProcessAttachedQueueEvent) { |
881 | 0 | mProcessAttachedQueueEvent->Revoke(); |
882 | 0 | } |
883 | 0 |
|
884 | 0 | if (mBoundContentSet) { |
885 | 0 | mBoundContentSet->Clear(); |
886 | 0 | } |
887 | 0 |
|
888 | 0 | mDocument = nullptr; |
889 | 0 | } |
890 | | |
891 | | void |
892 | | nsBindingManager::Traverse(nsIContent *aContent, |
893 | | nsCycleCollectionTraversalCallback &cb) |
894 | 0 | { |
895 | 0 | if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) || |
896 | 0 | !aContent->IsElement()) { |
897 | 0 | // Don't traverse if content is not in this binding manager. |
898 | 0 | // We also don't traverse non-elements because there should not |
899 | 0 | // be bindings (checking the flag alone is not sufficient because |
900 | 0 | // the flag is also set on children of insertion points that may be |
901 | 0 | // non-elements). |
902 | 0 | return; |
903 | 0 | } |
904 | 0 | |
905 | 0 | if (mBoundContentSet && mBoundContentSet->Contains(aContent)) { |
906 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBoundContentSet entry"); |
907 | 0 | cb.NoteXPCOMChild(aContent); |
908 | 0 | } |
909 | 0 |
|
910 | 0 | nsIXPConnectWrappedJS *value = GetWrappedJS(aContent); |
911 | 0 | if (value) { |
912 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key"); |
913 | 0 | cb.NoteXPCOMChild(aContent); |
914 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value"); |
915 | 0 | cb.NoteXPCOMChild(value); |
916 | 0 | } |
917 | 0 | } |
918 | | |
919 | | void |
920 | | nsBindingManager::HandleChildInsertion(nsIContent* aContainer, |
921 | | nsIContent* aChild, |
922 | | bool aAppend) |
923 | 0 | { |
924 | 0 | MOZ_ASSERT(aChild, "Must have child"); |
925 | 0 |
|
926 | 0 | XBLChildrenElement* point = nullptr; |
927 | 0 | nsIContent* parent = aContainer; |
928 | 0 |
|
929 | 0 | // Handle insertion of default content. |
930 | 0 | if (parent && parent->IsActiveChildrenElement()) { |
931 | 0 | XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent); |
932 | 0 | if (childrenEl->HasInsertedChildren()) { |
933 | 0 | // Inserting default content that isn't being used. Ignore. |
934 | 0 | return; |
935 | 0 | } |
936 | 0 | |
937 | 0 | childrenEl->MaybeSetupDefaultContent(); |
938 | 0 | parent = childrenEl->GetParent(); |
939 | 0 | } |
940 | 0 |
|
941 | 0 | while (parent) { |
942 | 0 | nsXBLBinding* binding = GetBindingWithContent(parent); |
943 | 0 | if (!binding) { |
944 | 0 | break; |
945 | 0 | } |
946 | 0 | |
947 | 0 | point = binding->FindInsertionPointFor(aChild); |
948 | 0 | if (!point) { |
949 | 0 | break; |
950 | 0 | } |
951 | 0 | |
952 | 0 | // Insert the child into the proper insertion point. |
953 | 0 | // TODO If there were multiple insertion points, this approximation can be |
954 | 0 | // wrong. We need to re-run the distribution algorithm. In the meantime, |
955 | 0 | // this should work well enough. |
956 | 0 | uint32_t index = aAppend ? point->InsertedChildrenLength() : 0; |
957 | 0 | for (nsIContent* currentSibling = aChild->GetPreviousSibling(); |
958 | 0 | currentSibling; |
959 | 0 | currentSibling = currentSibling->GetPreviousSibling()) { |
960 | 0 | // If we find one of our previous siblings in the insertion point, the |
961 | 0 | // index following it is the correct insertion point. Otherwise, we guess |
962 | 0 | // based on whether we're appending or inserting. |
963 | 0 | int32_t pointIndex = point->IndexOfInsertedChild(currentSibling); |
964 | 0 | if (pointIndex != -1) { |
965 | 0 | index = pointIndex + 1; |
966 | 0 | break; |
967 | 0 | } |
968 | 0 | } |
969 | 0 |
|
970 | 0 | point->InsertInsertedChildAt(aChild, index); |
971 | 0 |
|
972 | 0 | nsIContent* newParent = point->GetParent(); |
973 | 0 | if (newParent == parent) { |
974 | 0 | break; |
975 | 0 | } |
976 | 0 | |
977 | 0 | parent = newParent; |
978 | 0 | } |
979 | 0 | } |
980 | | |
981 | | nsIContent* |
982 | | nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer, |
983 | | bool* aMulti) |
984 | 0 | { |
985 | 0 | *aMulti = false; |
986 | 0 |
|
987 | 0 | nsIContent* parent = aContainer; |
988 | 0 | if (aContainer->IsActiveChildrenElement()) { |
989 | 0 | if (static_cast<XBLChildrenElement*>(aContainer)-> |
990 | 0 | HasInsertedChildren()) { |
991 | 0 | return nullptr; |
992 | 0 | } |
993 | 0 | parent = aContainer->GetParent(); |
994 | 0 | } |
995 | 0 |
|
996 | 0 | while(parent) { |
997 | 0 | nsXBLBinding* binding = GetBindingWithContent(parent); |
998 | 0 | if (!binding) { |
999 | 0 | break; |
1000 | 0 | } |
1001 | 0 | |
1002 | 0 | if (binding->HasFilteredInsertionPoints()) { |
1003 | 0 | *aMulti = true; |
1004 | 0 | return nullptr; |
1005 | 0 | } |
1006 | 0 | |
1007 | 0 | XBLChildrenElement* point = binding->GetDefaultInsertionPoint(); |
1008 | 0 | if (!point) { |
1009 | 0 | return nullptr; |
1010 | 0 | } |
1011 | 0 | |
1012 | 0 | nsIContent* newParent = point->GetParent(); |
1013 | 0 | if (newParent == parent) { |
1014 | 0 | break; |
1015 | 0 | } |
1016 | 0 | parent = newParent; |
1017 | 0 | } |
1018 | 0 |
|
1019 | 0 | return parent; |
1020 | 0 | } |
1021 | | |
1022 | | size_t |
1023 | | nsBindingManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
1024 | 0 | { |
1025 | 0 | size_t n = aMallocSizeOf(this); |
1026 | 0 |
|
1027 | 0 | #define SHALLOW_SIZE_INCLUDING(field_) \ |
1028 | 0 | n += field_ ? field_->ShallowSizeOfIncludingThis(aMallocSizeOf) : 0; |
1029 | 0 | SHALLOW_SIZE_INCLUDING(mBoundContentSet); |
1030 | 0 | SHALLOW_SIZE_INCLUDING(mWrapperTable); |
1031 | 0 | SHALLOW_SIZE_INCLUDING(mLoadingDocTable); |
1032 | 0 | #undef SHALLOW_SIZE_INCLUDING |
1033 | 0 | n += mAttachedStack.ShallowSizeOfExcludingThis(aMallocSizeOf); |
1034 | 0 |
|
1035 | 0 | if (mDocumentTable) { |
1036 | 0 | n += mDocumentTable->ShallowSizeOfIncludingThis(aMallocSizeOf); |
1037 | 0 | #ifdef MOZ_XUL |
1038 | 0 | nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); |
1039 | 0 | #endif |
1040 | 0 | for (auto iter = mDocumentTable->Iter(); !iter.Done(); iter.Next()) { |
1041 | 0 | nsXBLDocumentInfo* docInfo = iter.UserData(); |
1042 | 0 | #ifdef MOZ_XUL |
1043 | 0 | nsXBLDocumentInfo* cachedInfo = cache->GetXBLDocumentInfo(iter.Key()); |
1044 | 0 | if (cachedInfo == docInfo) { |
1045 | 0 | // If this binding has been cached, skip it since it can be |
1046 | 0 | // reused by other documents. |
1047 | 0 | continue; |
1048 | 0 | } |
1049 | 0 | #endif |
1050 | 0 | n += docInfo->SizeOfIncludingThis(aMallocSizeOf); |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 |
|
1054 | 0 | return n; |
1055 | 0 | } |