Bumping manifests a=b2g-bump
[gecko.git] / dom / xbl / nsBindingManager.cpp
blob8c54a60afba10127cfb425046d589935af580f07
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=79: */
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 #include "nsBindingManager.h"
9 #include "nsCOMPtr.h"
10 #include "nsXBLService.h"
11 #include "nsIInputStream.h"
12 #include "nsIURI.h"
13 #include "nsIURL.h"
14 #include "nsIChannel.h"
15 #include "nsXPIDLString.h"
16 #include "nsNetUtil.h"
17 #include "plstr.h"
18 #include "nsIContent.h"
19 #include "nsIDOMElement.h"
20 #include "nsIDocument.h"
21 #include "nsContentUtils.h"
22 #include "nsIPresShell.h"
23 #include "nsIXMLContentSink.h"
24 #include "nsContentCID.h"
25 #include "mozilla/dom/XMLDocument.h"
26 #include "nsIStreamListener.h"
27 #include "ChildIterator.h"
28 #include "nsITimer.h"
30 #include "nsXBLBinding.h"
31 #include "nsXBLPrototypeBinding.h"
32 #include "nsXBLDocumentInfo.h"
33 #include "mozilla/dom/XBLChildrenElement.h"
35 #include "nsIStyleRuleProcessor.h"
36 #include "nsRuleProcessorData.h"
37 #include "nsIWeakReference.h"
39 #include "nsWrapperCacheInlines.h"
40 #include "nsIXPConnect.h"
41 #include "nsDOMCID.h"
42 #include "nsIDOMScriptObjectFactory.h"
43 #include "nsIScriptGlobalObject.h"
44 #include "nsTHashtable.h"
46 #include "nsIScriptContext.h"
47 #include "xpcpublic.h"
48 #include "jswrapper.h"
50 #include "nsThreadUtils.h"
51 #include "mozilla/dom/NodeListBinding.h"
52 #include "mozilla/dom/ScriptSettings.h"
54 using namespace mozilla;
55 using namespace mozilla::dom;
57 // Implement our nsISupports methods
59 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
61 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager)
62 tmp->mDestroyed = true;
64 if (tmp->mBoundContentSet)
65 tmp->mBoundContentSet->Clear();
67 if (tmp->mDocumentTable)
68 tmp->mDocumentTable->Clear();
70 if (tmp->mLoadingDocTable)
71 tmp->mLoadingDocTable->Clear();
73 if (tmp->mWrapperTable) {
74 tmp->mWrapperTable->Clear();
75 tmp->mWrapperTable = nullptr;
78 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttachedStack)
80 if (tmp->mProcessAttachedQueueEvent) {
81 tmp->mProcessAttachedQueueEvent->Revoke();
83 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
86 static PLDHashOperator
87 DocumentInfoHashtableTraverser(nsIURI* key,
88 nsXBLDocumentInfo* di,
89 void* userArg)
91 nsCycleCollectionTraversalCallback *cb =
92 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
93 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mDocumentTable value");
94 cb->NoteXPCOMChild(di);
95 return PL_DHASH_NEXT;
98 static PLDHashOperator
99 LoadingDocHashtableTraverser(nsIURI* key,
100 nsIStreamListener* sl,
101 void* userArg)
103 nsCycleCollectionTraversalCallback *cb =
104 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
105 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mLoadingDocTable value");
106 cb->NoteXPCOMChild(sl);
107 return PL_DHASH_NEXT;
110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager)
111 // The hashes keyed on nsIContent are traversed from the nsIContent itself.
112 if (tmp->mDocumentTable)
113 tmp->mDocumentTable->EnumerateRead(&DocumentInfoHashtableTraverser, &cb);
114 if (tmp->mLoadingDocTable)
115 tmp->mLoadingDocTable->EnumerateRead(&LoadingDocHashtableTraverser, &cb);
116 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttachedStack)
117 // No need to traverse mProcessAttachedQueueEvent, since it'll just
118 // fire at some point or become revoke and drop its ref to us.
119 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
121 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager)
122 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
123 NS_INTERFACE_MAP_ENTRY(nsISupports)
124 NS_INTERFACE_MAP_END
126 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager)
127 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager)
129 // Constructors/Destructors
130 nsBindingManager::nsBindingManager(nsIDocument* aDocument)
131 : mProcessingAttachedStack(false),
132 mDestroyed(false),
133 mAttachedStackSizeOnOutermost(0),
134 mDocument(aDocument)
138 nsBindingManager::~nsBindingManager(void)
140 mDestroyed = true;
143 nsXBLBinding*
144 nsBindingManager::GetBindingWithContent(nsIContent* aContent)
146 nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr;
147 return binding ? binding->GetBindingWithContent() : nullptr;
150 void
151 nsBindingManager::AddBoundContent(nsIContent* aContent)
153 if (!mBoundContentSet) {
154 mBoundContentSet = new nsTHashtable<nsRefPtrHashKey<nsIContent> >;
156 mBoundContentSet->PutEntry(aContent);
159 void
160 nsBindingManager::RemoveBoundContent(nsIContent* aContent)
162 if (mBoundContentSet) {
163 mBoundContentSet->RemoveEntry(aContent);
166 // The death of the bindings means the death of the JS wrapper.
167 SetWrappedJS(aContent, nullptr);
170 nsIXPConnectWrappedJS*
171 nsBindingManager::GetWrappedJS(nsIContent* aContent)
173 if (!mWrapperTable) {
174 return nullptr;
177 if (!aContent || !aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
178 return nullptr;
181 return mWrapperTable->GetWeak(aContent);
184 nsresult
185 nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
187 if (mDestroyed) {
188 return NS_OK;
191 if (aWrappedJS) {
192 // lazily create the table, but only when adding elements
193 if (!mWrapperTable) {
194 mWrapperTable = new WrapperHashtable();
196 aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
198 NS_ASSERTION(aContent, "key must be non-null");
199 if (!aContent) return NS_ERROR_INVALID_ARG;
201 mWrapperTable->Put(aContent, aWrappedJS);
203 return NS_OK;
206 // no value, so remove the key from the table
207 if (mWrapperTable) {
208 mWrapperTable->Remove(aContent);
211 return NS_OK;
214 void
215 nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
216 nsIDocument* aOldDocument)
218 NS_PRECONDITION(aOldDocument != nullptr, "no old document");
220 nsRefPtr<nsXBLBinding> binding = aContent->GetXBLBinding();
221 if (binding) {
222 // The binding manager may have been destroyed before a runnable
223 // has had a chance to reach this point. If so, we bail out on calling
224 // BindingDetached (which may invoke a XBL destructor) and
225 // ChangeDocument, but we still want to clear out the binding
226 // and insertion parent that may hold references.
227 if (!mDestroyed) {
228 binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement());
229 binding->ChangeDocument(aOldDocument, nullptr);
232 aContent->SetXBLBinding(nullptr, this);
235 // Clear out insertion parent and content lists.
236 aContent->SetXBLInsertionParent(nullptr);
239 nsIAtom*
240 nsBindingManager::ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID)
242 nsXBLBinding *binding = aContent->GetXBLBinding();
244 if (binding) {
245 nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
247 if (base) {
248 return base;
252 *aNameSpaceID = aContent->GetNameSpaceID();
253 return aContent->Tag();
256 nsresult
257 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
258 nsIDOMNodeList** aResult)
260 NS_IF_ADDREF(*aResult = GetAnonymousNodesFor(aContent));
261 return NS_OK;
264 nsINodeList*
265 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent)
267 nsXBLBinding* binding = GetBindingWithContent(aContent);
268 return binding ? binding->GetAnonymousNodeList() : nullptr;
271 nsresult
272 nsBindingManager::ClearBinding(nsIContent* aContent)
274 // Hold a ref to the binding so it won't die when we remove it from our table
275 nsRefPtr<nsXBLBinding> binding =
276 aContent ? aContent->GetXBLBinding() : nullptr;
278 if (!binding) {
279 return NS_OK;
282 // For now we can only handle removing a binding if it's the only one
283 NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE);
285 // Hold strong ref in case removing the binding tries to close the
286 // window or something.
287 // XXXbz should that be ownerdoc? Wouldn't we need a ref to the
288 // currentdoc too? What's the one that should be passed to
289 // ChangeDocument?
290 nsCOMPtr<nsIDocument> doc = aContent->OwnerDoc();
292 // Finally remove the binding...
293 // XXXbz this doesn't remove the implementation! Should fix! Until
294 // then we need the explicit UnhookEventHandlers here.
295 binding->UnhookEventHandlers();
296 binding->ChangeDocument(doc, nullptr);
297 aContent->SetXBLBinding(nullptr, this);
298 binding->MarkForDeath();
300 // ...and recreate its frames. We need to do this since the frames may have
301 // been removed and style may have changed due to the removal of the
302 // anonymous children.
303 // XXXbz this should be using the current doc (if any), not the owner doc.
304 nsIPresShell *presShell = doc->GetShell();
305 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
307 return presShell->RecreateFramesFor(aContent);;
310 nsresult
311 nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
312 nsIURI* aURL,
313 nsIPrincipal* aOriginPrincipal)
315 NS_PRECONDITION(aURL, "Must have a URI to load!");
317 // First we need to load our binding.
318 nsXBLService* xblService = nsXBLService::GetInstance();
319 if (!xblService)
320 return NS_ERROR_FAILURE;
322 // Load the binding doc.
323 nsRefPtr<nsXBLDocumentInfo> info;
324 xblService->LoadBindingDocumentInfo(nullptr, aBoundDoc, aURL,
325 aOriginPrincipal, true,
326 getter_AddRefs(info));
327 if (!info)
328 return NS_ERROR_FAILURE;
330 return NS_OK;
333 void
334 nsBindingManager::RemoveFromAttachedQueue(nsXBLBinding* aBinding)
336 // Don't remove items here as that could mess up an executing
337 // ProcessAttachedQueue. Instead, null the entry in the queue.
338 size_t index = mAttachedStack.IndexOf(aBinding);
339 if (index != mAttachedStack.NoIndex) {
340 mAttachedStack[index] = nullptr;
344 nsresult
345 nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
347 if (!mAttachedStack.AppendElement(aBinding))
348 return NS_ERROR_OUT_OF_MEMORY;
350 // If we're in the middle of processing our queue already, don't
351 // bother posting the event.
352 if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) {
353 PostProcessAttachedQueueEvent();
356 // Make sure that flushes will flush out the new items as needed.
357 mDocument->SetNeedStyleFlush();
359 return NS_OK;
363 void
364 nsBindingManager::PostProcessAttachedQueueEvent()
366 mProcessAttachedQueueEvent =
367 NS_NewRunnableMethod(this, &nsBindingManager::DoProcessAttachedQueue);
368 nsresult rv = NS_DispatchToCurrentThread(mProcessAttachedQueueEvent);
369 if (NS_SUCCEEDED(rv) && mDocument) {
370 mDocument->BlockOnload();
374 // static
375 void
376 nsBindingManager::PostPAQEventCallback(nsITimer* aTimer, void* aClosure)
378 nsRefPtr<nsBindingManager> mgr =
379 already_AddRefed<nsBindingManager>(static_cast<nsBindingManager*>(aClosure));
380 mgr->PostProcessAttachedQueueEvent();
381 NS_RELEASE(aTimer);
384 void
385 nsBindingManager::DoProcessAttachedQueue()
387 if (!mProcessingAttachedStack) {
388 ProcessAttachedQueue();
390 NS_ASSERTION(mAttachedStack.Length() == 0,
391 "Shouldn't have pending bindings!");
393 mProcessAttachedQueueEvent = nullptr;
394 } else {
395 // Someone's doing event processing from inside a constructor.
396 // They're evil, but we'll fight back! Just poll on them being
397 // done and repost the attached queue event.
399 // But don't poll in a tight loop -- otherwise we keep the Gecko
400 // event loop non-empty and trigger bug 1021240 on OS X.
401 nsresult rv = NS_ERROR_FAILURE;
402 nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
403 if (timer) {
404 rv = timer->InitWithFuncCallback(PostPAQEventCallback, this,
405 100, nsITimer::TYPE_ONE_SHOT);
407 if (NS_SUCCEEDED(rv)) {
408 NS_ADDREF_THIS();
409 NS_ADDREF(timer);
413 // No matter what, unblock onload for the event that's fired.
414 if (mDocument) {
415 // Hold a strong reference while calling UnblockOnload since that might
416 // run script.
417 nsCOMPtr<nsIDocument> doc = mDocument;
418 doc->UnblockOnload(true);
422 void
423 nsBindingManager::ProcessAttachedQueue(uint32_t aSkipSize)
425 if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
426 return;
428 mProcessingAttachedStack = true;
430 // Excute constructors. Do this from high index to low
431 while (mAttachedStack.Length() > aSkipSize) {
432 uint32_t lastItem = mAttachedStack.Length() - 1;
433 nsRefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem);
434 mAttachedStack.RemoveElementAt(lastItem);
435 if (binding) {
436 binding->ExecuteAttachedHandler();
440 // If NodeWillBeDestroyed has run we don't want to clobber
441 // mProcessingAttachedStack set there.
442 if (mDocument) {
443 mProcessingAttachedStack = false;
446 NS_ASSERTION(mAttachedStack.Length() == aSkipSize, "How did we get here?");
448 mAttachedStack.Compact();
451 // Keep bindings and bound elements alive while executing detached handlers.
452 struct BindingTableReadClosure
454 nsCOMArray<nsIContent> mBoundElements;
455 nsBindingList mBindings;
458 static PLDHashOperator
459 AccumulateBindingsToDetach(nsRefPtrHashKey<nsIContent> *aKey,
460 void* aClosure)
462 nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
463 BindingTableReadClosure* closure =
464 static_cast<BindingTableReadClosure*>(aClosure);
465 if (binding && closure->mBindings.AppendElement(binding)) {
466 if (!closure->mBoundElements.AppendObject(binding->GetBoundElement())) {
467 closure->mBindings.RemoveElementAt(closure->mBindings.Length() - 1);
470 return PL_DHASH_NEXT;
473 void
474 nsBindingManager::ExecuteDetachedHandlers()
476 // Walk our hashtable of bindings.
477 if (mBoundContentSet) {
478 BindingTableReadClosure closure;
479 mBoundContentSet->EnumerateEntries(AccumulateBindingsToDetach, &closure);
480 uint32_t i, count = closure.mBindings.Length();
481 for (i = 0; i < count; ++i) {
482 closure.mBindings[i]->ExecuteDetachedHandler();
487 nsresult
488 nsBindingManager::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
490 NS_PRECONDITION(aDocumentInfo, "Must have a non-null documentinfo!");
492 if (!mDocumentTable) {
493 mDocumentTable = new nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>();
496 mDocumentTable->Put(aDocumentInfo->DocumentURI(), aDocumentInfo);
498 return NS_OK;
501 void
502 nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
504 if (mDocumentTable) {
505 mDocumentTable->Remove(aDocumentInfo->DocumentURI());
509 nsXBLDocumentInfo*
510 nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL)
512 if (!mDocumentTable)
513 return nullptr;
515 return mDocumentTable->GetWeak(aURL);
518 nsresult
519 nsBindingManager::PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener)
521 NS_PRECONDITION(aListener, "Must have a non-null listener!");
523 if (!mLoadingDocTable) {
524 mLoadingDocTable =
525 new nsInterfaceHashtable<nsURIHashKey,nsIStreamListener>();
527 mLoadingDocTable->Put(aURL, aListener);
529 return NS_OK;
532 nsIStreamListener*
533 nsBindingManager::GetLoadingDocListener(nsIURI* aURL)
535 if (!mLoadingDocTable)
536 return nullptr;
538 return mLoadingDocTable->GetWeak(aURL);
541 void
542 nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL)
544 if (mLoadingDocTable) {
545 mLoadingDocTable->Remove(aURL);
549 static PLDHashOperator
550 MarkForDeath(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
552 nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
554 if (binding->MarkedForDeath())
555 return PL_DHASH_NEXT; // Already marked for death.
557 nsAutoCString path;
558 binding->PrototypeBinding()->DocURI()->GetPath(path);
560 if (!strncmp(path.get(), "/skin", 5))
561 binding->MarkForDeath();
563 return PL_DHASH_NEXT;
566 void
567 nsBindingManager::FlushSkinBindings()
569 if (mBoundContentSet) {
570 mBoundContentSet->EnumerateEntries(MarkForDeath, nullptr);
574 // Used below to protect from recurring in QI calls through XPConnect.
575 struct AntiRecursionData {
576 nsIContent* element;
577 REFNSIID iid;
578 AntiRecursionData* next;
580 AntiRecursionData(nsIContent* aElement,
581 REFNSIID aIID,
582 AntiRecursionData* aNext)
583 : element(aElement), iid(aIID), next(aNext) {}
586 nsresult
587 nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
588 void** aResult)
590 *aResult = nullptr;
591 nsXBLBinding *binding = aContent ? aContent->GetXBLBinding() : nullptr;
592 if (binding) {
593 // The binding should not be asked for nsISupports
594 NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports");
595 if (binding->ImplementsInterface(aIID)) {
596 nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent);
598 if (wrappedJS) {
599 // Protect from recurring in QI calls through XPConnect.
600 // This can happen when a second binding is being resolved.
601 // At that point a wrappedJS exists, but it doesn't yet know about
602 // the iid we are asking for. So, without this protection,
603 // AggregatedQueryInterface would end up recurring back into itself
604 // through this code.
606 // With this protection, when we detect the recursion we return
607 // NS_NOINTERFACE in the inner call. The outer call will then fall
608 // through (see below) and build a new chained wrappedJS for the iid.
610 // We're careful to not assume that only one direct nesting can occur
611 // because there is a call into JS in the middle and we can't assume
612 // that this code won't be reached by some more complex nesting path.
614 // NOTE: We *assume* this is single threaded, so we can use a
615 // static linked list to do the check.
617 static AntiRecursionData* list = nullptr;
619 for (AntiRecursionData* p = list; p; p = p->next) {
620 if (p->element == aContent && p->iid.Equals(aIID)) {
621 *aResult = nullptr;
622 return NS_NOINTERFACE;
626 AntiRecursionData item(aContent, aIID, list);
627 list = &item;
629 nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
631 list = item.next;
633 if (*aResult)
634 return rv;
636 // No result was found, so this must be another XBL interface.
637 // Fall through to create a new wrapper.
640 // We have never made a wrapper for this implementation.
641 // Create an XPC wrapper for the script object and hand it back.
642 AutoJSAPI jsapi;
643 jsapi.Init();
644 JSContext* cx = jsapi.cx();
646 nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
648 JS::Rooted<JSObject*> jsobj(cx, aContent->GetWrapper());
649 NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE);
651 // If we're using an XBL scope, we need to use the Xray view to the bound
652 // content in order to view the full array of methods defined in the
653 // binding, some of which may not be exposed on the prototype of
654 // untrusted content. We don't need to consider add-on scopes here
655 // because they're chrome-only and no Xrays are involved.
657 // If there's no separate XBL scope, or if the reflector itself lives in
658 // the XBL scope, we'll end up with the global of the reflector.
659 JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, jsobj));
660 NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED);
661 JSAutoCompartment ac(cx, xblScope);
662 bool ok = JS_WrapObject(cx, &jsobj);
663 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
664 MOZ_ASSERT_IF(js::IsWrapper(jsobj), xpc::IsXrayWrapper(jsobj));
666 nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, cx,
667 jsobj, aIID, aResult);
668 if (NS_FAILED(rv))
669 return rv;
671 // We successfully created a wrapper. We will own this wrapper for as long as the binding remains
672 // alive. At the time the binding is cleared out of the bindingManager, we will remove the wrapper
673 // from the bindingManager as well.
674 nsISupports* supp = static_cast<nsISupports*>(*aResult);
675 wrappedJS = do_QueryInterface(supp);
676 SetWrappedJS(aContent, wrappedJS);
678 return rv;
682 *aResult = nullptr;
683 return NS_NOINTERFACE;
686 nsresult
687 nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
688 ElementDependentRuleProcessorData* aData,
689 bool* aCutOffInheritance)
691 *aCutOffInheritance = false;
693 NS_ASSERTION(aData->mElement, "How did that happen?");
695 // Walk the binding scope chain, starting with the binding attached to our
696 // content, up till we run out of scopes or we get cut off.
697 nsIContent *content = aData->mElement;
699 do {
700 nsXBLBinding *binding = content->GetXBLBinding();
701 if (binding) {
702 aData->mTreeMatchContext.mScopedRoot = content;
703 binding->WalkRules(aFunc, aData);
704 // If we're not looking at our original content, allow the binding to cut
705 // off style inheritance
706 if (content != aData->mElement) {
707 if (!binding->InheritsStyle()) {
708 // Go no further; we're not inheriting style from anything above here
709 break;
714 if (content->IsRootOfNativeAnonymousSubtree()) {
715 break; // Deliberately cut off style inheritance here.
718 content = content->GetBindingParent();
719 } while (content);
721 // If "content" is non-null that means we cut off inheritance at some point
722 // in the loop.
723 *aCutOffInheritance = (content != nullptr);
725 // Null out the scoped root that we set repeatedly
726 aData->mTreeMatchContext.mScopedRoot = nullptr;
728 return NS_OK;
731 typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
733 static PLDHashOperator
734 EnumRuleProcessors(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
736 nsIContent *boundContent = aKey->GetKey();
737 nsAutoPtr<RuleProcessorSet> *set = static_cast<nsAutoPtr<RuleProcessorSet>*>(aClosure);
738 for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
739 binding = binding->GetBaseBinding()) {
740 nsIStyleRuleProcessor *ruleProc =
741 binding->PrototypeBinding()->GetRuleProcessor();
742 if (ruleProc) {
743 if (!(*set)) {
744 *set = new RuleProcessorSet;
746 (*set)->PutEntry(ruleProc);
749 return PL_DHASH_NEXT;
752 struct WalkAllRulesData {
753 nsIStyleRuleProcessor::EnumFunc mFunc;
754 ElementDependentRuleProcessorData* mData;
757 static PLDHashOperator
758 EnumWalkAllRules(nsPtrHashKey<nsIStyleRuleProcessor> *aKey, void* aClosure)
760 nsIStyleRuleProcessor *ruleProcessor = aKey->GetKey();
762 WalkAllRulesData *data = static_cast<WalkAllRulesData*>(aClosure);
764 (*(data->mFunc))(ruleProcessor, data->mData);
766 return PL_DHASH_NEXT;
769 void
770 nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
771 ElementDependentRuleProcessorData* aData)
773 if (!mBoundContentSet) {
774 return;
777 nsAutoPtr<RuleProcessorSet> set;
778 mBoundContentSet->EnumerateEntries(EnumRuleProcessors, &set);
779 if (!set)
780 return;
782 WalkAllRulesData data = { aFunc, aData };
783 set->EnumerateEntries(EnumWalkAllRules, &data);
786 struct MediumFeaturesChangedData {
787 nsPresContext *mPresContext;
788 bool *mRulesChanged;
791 static PLDHashOperator
792 EnumMediumFeaturesChanged(nsPtrHashKey<nsIStyleRuleProcessor> *aKey, void* aClosure)
794 nsIStyleRuleProcessor *ruleProcessor = aKey->GetKey();
796 MediumFeaturesChangedData *data =
797 static_cast<MediumFeaturesChangedData*>(aClosure);
799 bool thisChanged = ruleProcessor->MediumFeaturesChanged(data->mPresContext);
800 *data->mRulesChanged = *data->mRulesChanged || thisChanged;
802 return PL_DHASH_NEXT;
805 nsresult
806 nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
807 bool* aRulesChanged)
809 *aRulesChanged = false;
810 if (!mBoundContentSet) {
811 return NS_OK;
814 nsAutoPtr<RuleProcessorSet> set;
815 mBoundContentSet->EnumerateEntries(EnumRuleProcessors, &set);
816 if (!set) {
817 return NS_OK;
820 MediumFeaturesChangedData data = { aPresContext, aRulesChanged };
821 set->EnumerateEntries(EnumMediumFeaturesChanged, &data);
822 return NS_OK;
825 static PLDHashOperator
826 EnumAppendAllSheets(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
828 nsIContent *boundContent = aKey->GetKey();
829 nsTArray<CSSStyleSheet*>* array =
830 static_cast<nsTArray<CSSStyleSheet*>*>(aClosure);
831 for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
832 binding = binding->GetBaseBinding()) {
833 binding->PrototypeBinding()->AppendStyleSheetsTo(*array);
835 return PL_DHASH_NEXT;
838 void
839 nsBindingManager::AppendAllSheets(nsTArray<CSSStyleSheet*>& aArray)
841 if (mBoundContentSet) {
842 mBoundContentSet->EnumerateEntries(EnumAppendAllSheets, &aArray);
846 static void
847 InsertAppendedContent(XBLChildrenElement* aPoint,
848 nsIContent* aFirstNewContent)
850 int32_t insertionIndex;
851 if (nsIContent* prevSibling = aFirstNewContent->GetPreviousSibling()) {
852 // If we have a previous sibling, then it must already be in aPoint. Find
853 // it and insert after it.
854 insertionIndex = aPoint->IndexOfInsertedChild(prevSibling);
855 MOZ_ASSERT(insertionIndex != -1);
857 // Our insertion index is one after our previous sibling's index.
858 ++insertionIndex;
859 } else {
860 // Otherwise, we append.
861 // TODO This is wrong for nested insertion points. In that case, we need to
862 // keep track of the right index to insert into.
863 insertionIndex = aPoint->InsertedChildrenLength();
866 // Do the inserting.
867 for (nsIContent* currentChild = aFirstNewContent;
868 currentChild;
869 currentChild = currentChild->GetNextSibling()) {
870 aPoint->InsertInsertedChildAt(currentChild, insertionIndex++);
874 void
875 nsBindingManager::ContentAppended(nsIDocument* aDocument,
876 nsIContent* aContainer,
877 nsIContent* aFirstNewContent,
878 int32_t aNewIndexInContainer)
880 if (aNewIndexInContainer == -1) {
881 return;
884 // Try to find insertion points for all the new kids.
885 XBLChildrenElement* point = nullptr;
886 nsIContent* parent = aContainer;
888 // Handle appending of default content.
889 if (parent && parent->IsActiveChildrenElement()) {
890 XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
891 if (childrenEl->HasInsertedChildren()) {
892 // Appending default content that isn't being used. Ignore.
893 return;
896 childrenEl->MaybeSetupDefaultContent();
897 parent = childrenEl->GetParent();
900 bool first = true;
901 do {
902 nsXBLBinding* binding = GetBindingWithContent(parent);
903 if (!binding) {
904 break;
907 if (binding->HasFilteredInsertionPoints()) {
908 // There are filtered insertion points involved, handle each child
909 // separately.
910 // We could optimize this in the case when we've nested a few levels
911 // deep already, without hitting bindings that have filtered insertion
912 // points.
913 int32_t currentIndex = aNewIndexInContainer;
914 for (nsIContent* currentChild = aFirstNewContent; currentChild;
915 currentChild = currentChild->GetNextSibling()) {
916 HandleChildInsertion(aContainer, currentChild,
917 currentIndex++, true);
920 return;
923 point = binding->GetDefaultInsertionPoint();
924 if (!point) {
925 break;
928 // Even though we're in ContentAppended, nested insertion points force us
929 // to deal with this append as an insertion except in the outermost
930 // binding.
931 if (first) {
932 first = false;
933 for (nsIContent* child = aFirstNewContent; child;
934 child = child->GetNextSibling()) {
935 point->AppendInsertedChild(child);
937 } else {
938 InsertAppendedContent(point, aFirstNewContent);
941 nsIContent* newParent = point->GetParent();
942 if (newParent == parent) {
943 break;
945 parent = newParent;
946 } while (parent);
949 void
950 nsBindingManager::ContentInserted(nsIDocument* aDocument,
951 nsIContent* aContainer,
952 nsIContent* aChild,
953 int32_t aIndexInContainer)
955 if (aIndexInContainer == -1) {
956 return;
959 HandleChildInsertion(aContainer, aChild, aIndexInContainer, false);
962 void
963 nsBindingManager::ContentRemoved(nsIDocument* aDocument,
964 nsIContent* aContainer,
965 nsIContent* aChild,
966 int32_t aIndexInContainer,
967 nsIContent* aPreviousSibling)
969 aChild->SetXBLInsertionParent(nullptr);
971 XBLChildrenElement* point = nullptr;
972 nsIContent* parent = aContainer;
974 // Handle appending of default content.
975 if (parent && parent->IsActiveChildrenElement()) {
976 XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
977 if (childrenEl->HasInsertedChildren()) {
978 // Removing default content that isn't being used. Ignore.
979 return;
982 parent = childrenEl->GetParent();
985 do {
986 nsXBLBinding* binding = GetBindingWithContent(parent);
987 if (!binding) {
988 // If aChild is XBL content, it might have <xbl:children> elements
989 // somewhere under it. We need to inform those elements that they're no
990 // longer in the tree so they can tell their distributed children that
991 // they're no longer distributed under them.
992 // XXX This is wrong. We need to do far more work to update the parent
993 // binding's list of insertion points and to get the new insertion parent
994 // for the newly-distributed children correct.
995 if (aChild->GetBindingParent()) {
996 ClearInsertionPointsRecursively(aChild);
998 return;
1001 point = binding->FindInsertionPointFor(aChild);
1002 if (!point) {
1003 break;
1006 point->RemoveInsertedChild(aChild);
1008 nsIContent* newParent = point->GetParent();
1009 if (newParent == parent) {
1010 break;
1012 parent = newParent;
1013 } while (parent);
1016 void
1017 nsBindingManager::ClearInsertionPointsRecursively(nsIContent* aContent)
1019 if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
1020 static_cast<XBLChildrenElement*>(aContent)->ClearInsertedChildren();
1023 for (nsIContent* child = aContent->GetFirstChild(); child;
1024 child = child->GetNextSibling()) {
1025 ClearInsertionPointsRecursively(child);
1029 void
1030 nsBindingManager::DropDocumentReference()
1032 mDestroyed = true;
1034 // Make sure to not run any more XBL constructors
1035 mProcessingAttachedStack = true;
1036 if (mProcessAttachedQueueEvent) {
1037 mProcessAttachedQueueEvent->Revoke();
1040 if (mBoundContentSet) {
1041 mBoundContentSet->Clear();
1044 mDocument = nullptr;
1047 void
1048 nsBindingManager::Traverse(nsIContent *aContent,
1049 nsCycleCollectionTraversalCallback &cb)
1051 if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
1052 !aContent->IsElement()) {
1053 // Don't traverse if content is not in this binding manager.
1054 // We also don't traverse non-elements because there should not
1055 // be bindings (checking the flag alone is not sufficient because
1056 // the flag is also set on children of insertion points that may be
1057 // non-elements).
1058 return;
1061 if (mBoundContentSet && mBoundContentSet->Contains(aContent)) {
1062 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBoundContentSet entry");
1063 cb.NoteXPCOMChild(aContent);
1066 nsIXPConnectWrappedJS *value = GetWrappedJS(aContent);
1067 if (value) {
1068 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
1069 cb.NoteXPCOMChild(aContent);
1070 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
1071 cb.NoteXPCOMChild(value);
1075 void
1076 nsBindingManager::BeginOutermostUpdate()
1078 mAttachedStackSizeOnOutermost = mAttachedStack.Length();
1081 void
1082 nsBindingManager::EndOutermostUpdate()
1084 if (!mProcessingAttachedStack) {
1085 ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
1086 mAttachedStackSizeOnOutermost = 0;
1090 void
1091 nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
1092 nsIContent* aChild,
1093 uint32_t aIndexInContainer,
1094 bool aAppend)
1096 NS_PRECONDITION(aChild, "Must have child");
1097 NS_PRECONDITION(!aContainer ||
1098 uint32_t(aContainer->IndexOf(aChild)) == aIndexInContainer,
1099 "Child not at the right index?");
1101 XBLChildrenElement* point = nullptr;
1102 nsIContent* parent = aContainer;
1104 // Handle insertion of default content.
1105 if (parent && parent->IsActiveChildrenElement()) {
1106 XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
1107 if (childrenEl->HasInsertedChildren()) {
1108 // Inserting default content that isn't being used. Ignore.
1109 return;
1112 childrenEl->MaybeSetupDefaultContent();
1113 parent = childrenEl->GetParent();
1116 while (parent) {
1117 nsXBLBinding* binding = GetBindingWithContent(parent);
1118 if (!binding) {
1119 break;
1122 point = binding->FindInsertionPointFor(aChild);
1123 if (!point) {
1124 break;
1127 // Insert the child into the proper insertion point.
1128 // TODO If there were multiple insertion points, this approximation can be
1129 // wrong. We need to re-run the distribution algorithm. In the meantime,
1130 // this should work well enough.
1131 uint32_t index = aAppend ? point->InsertedChildrenLength() : 0;
1132 for (nsIContent* currentSibling = aChild->GetPreviousSibling();
1133 currentSibling;
1134 currentSibling = currentSibling->GetPreviousSibling()) {
1135 // If we find one of our previous siblings in the insertion point, the
1136 // index following it is the correct insertion point. Otherwise, we guess
1137 // based on whether we're appending or inserting.
1138 int32_t pointIndex = point->IndexOfInsertedChild(currentSibling);
1139 if (pointIndex != -1) {
1140 index = pointIndex + 1;
1141 break;
1145 point->InsertInsertedChildAt(aChild, index);
1147 nsIContent* newParent = point->GetParent();
1148 if (newParent == parent) {
1149 break;
1152 parent = newParent;
1157 nsIContent*
1158 nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
1159 nsIContent* aChild)
1161 NS_PRECONDITION(aChild->GetParent() == aContainer,
1162 "Wrong container");
1164 nsIContent* parent = aContainer;
1165 if (aContainer->IsActiveChildrenElement()) {
1166 if (static_cast<XBLChildrenElement*>(aContainer)->
1167 HasInsertedChildren()) {
1168 return nullptr;
1170 parent = aContainer->GetParent();
1173 while (parent) {
1174 nsXBLBinding* binding = GetBindingWithContent(parent);
1175 if (!binding) {
1176 break;
1179 XBLChildrenElement* point = binding->FindInsertionPointFor(aChild);
1180 if (!point) {
1181 return nullptr;
1184 nsIContent* newParent = point->GetParent();
1185 if (newParent == parent) {
1186 break;
1188 parent = newParent;
1191 return parent;
1194 nsIContent*
1195 nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
1196 bool* aMulti)
1198 *aMulti = false;
1200 nsIContent* parent = aContainer;
1201 if (aContainer->IsActiveChildrenElement()) {
1202 if (static_cast<XBLChildrenElement*>(aContainer)->
1203 HasInsertedChildren()) {
1204 return nullptr;
1206 parent = aContainer->GetParent();
1209 while(parent) {
1210 nsXBLBinding* binding = GetBindingWithContent(parent);
1211 if (!binding) {
1212 break;
1215 if (binding->HasFilteredInsertionPoints()) {
1216 *aMulti = true;
1217 return nullptr;
1220 XBLChildrenElement* point = binding->GetDefaultInsertionPoint();
1221 if (!point) {
1222 return nullptr;
1225 nsIContent* newParent = point->GetParent();
1226 if (newParent == parent) {
1227 break;
1229 parent = newParent;
1232 return parent;