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"
10 #include "nsXBLService.h"
11 #include "nsIInputStream.h"
14 #include "nsIChannel.h"
15 #include "nsXPIDLString.h"
16 #include "nsNetUtil.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"
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"
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
,
91 nsCycleCollectionTraversalCallback
*cb
=
92 static_cast<nsCycleCollectionTraversalCallback
*>(userArg
);
93 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mDocumentTable value");
94 cb
->NoteXPCOMChild(di
);
98 static PLDHashOperator
99 LoadingDocHashtableTraverser(nsIURI
* key
,
100 nsIStreamListener
* sl
,
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
)
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),
133 mAttachedStackSizeOnOutermost(0),
138 nsBindingManager::~nsBindingManager(void)
144 nsBindingManager::GetBindingWithContent(nsIContent
* aContent
)
146 nsXBLBinding
* binding
= aContent
? aContent
->GetXBLBinding() : nullptr;
147 return binding
? binding
->GetBindingWithContent() : nullptr;
151 nsBindingManager::AddBoundContent(nsIContent
* aContent
)
153 if (!mBoundContentSet
) {
154 mBoundContentSet
= new nsTHashtable
<nsRefPtrHashKey
<nsIContent
> >;
156 mBoundContentSet
->PutEntry(aContent
);
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
) {
177 if (!aContent
|| !aContent
->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
181 return mWrapperTable
->GetWeak(aContent
);
185 nsBindingManager::SetWrappedJS(nsIContent
* aContent
, nsIXPConnectWrappedJS
* 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
);
206 // no value, so remove the key from the table
208 mWrapperTable
->Remove(aContent
);
215 nsBindingManager::RemovedFromDocumentInternal(nsIContent
* aContent
,
216 nsIDocument
* aOldDocument
)
218 NS_PRECONDITION(aOldDocument
!= nullptr, "no old document");
220 nsRefPtr
<nsXBLBinding
> binding
= aContent
->GetXBLBinding();
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.
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);
240 nsBindingManager::ResolveTag(nsIContent
* aContent
, int32_t* aNameSpaceID
)
242 nsXBLBinding
*binding
= aContent
->GetXBLBinding();
245 nsIAtom
* base
= binding
->GetBaseTag(aNameSpaceID
);
252 *aNameSpaceID
= aContent
->GetNameSpaceID();
253 return aContent
->Tag();
257 nsBindingManager::GetAnonymousNodesFor(nsIContent
* aContent
,
258 nsIDOMNodeList
** aResult
)
260 NS_IF_ADDREF(*aResult
= GetAnonymousNodesFor(aContent
));
265 nsBindingManager::GetAnonymousNodesFor(nsIContent
* aContent
)
267 nsXBLBinding
* binding
= GetBindingWithContent(aContent
);
268 return binding
? binding
->GetAnonymousNodeList() : nullptr;
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;
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
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
);;
311 nsBindingManager::LoadBindingDocument(nsIDocument
* aBoundDoc
,
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();
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
));
328 return NS_ERROR_FAILURE
;
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;
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();
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();
376 nsBindingManager::PostPAQEventCallback(nsITimer
* aTimer
, void* aClosure
)
378 nsRefPtr
<nsBindingManager
> mgr
=
379 already_AddRefed
<nsBindingManager
>(static_cast<nsBindingManager
*>(aClosure
));
380 mgr
->PostProcessAttachedQueueEvent();
385 nsBindingManager::DoProcessAttachedQueue()
387 if (!mProcessingAttachedStack
) {
388 ProcessAttachedQueue();
390 NS_ASSERTION(mAttachedStack
.Length() == 0,
391 "Shouldn't have pending bindings!");
393 mProcessAttachedQueueEvent
= nullptr;
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
);
404 rv
= timer
->InitWithFuncCallback(PostPAQEventCallback
, this,
405 100, nsITimer::TYPE_ONE_SHOT
);
407 if (NS_SUCCEEDED(rv
)) {
413 // No matter what, unblock onload for the event that's fired.
415 // Hold a strong reference while calling UnblockOnload since that might
417 nsCOMPtr
<nsIDocument
> doc
= mDocument
;
418 doc
->UnblockOnload(true);
423 nsBindingManager::ProcessAttachedQueue(uint32_t aSkipSize
)
425 if (mProcessingAttachedStack
|| mAttachedStack
.Length() <= aSkipSize
)
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
);
436 binding
->ExecuteAttachedHandler();
440 // If NodeWillBeDestroyed has run we don't want to clobber
441 // mProcessingAttachedStack set there.
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
,
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
;
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();
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
);
502 nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo
* aDocumentInfo
)
504 if (mDocumentTable
) {
505 mDocumentTable
->Remove(aDocumentInfo
->DocumentURI());
510 nsBindingManager::GetXBLDocumentInfo(nsIURI
* aURL
)
515 return mDocumentTable
->GetWeak(aURL
);
519 nsBindingManager::PutLoadingDocListener(nsIURI
* aURL
, nsIStreamListener
* aListener
)
521 NS_PRECONDITION(aListener
, "Must have a non-null listener!");
523 if (!mLoadingDocTable
) {
525 new nsInterfaceHashtable
<nsURIHashKey
,nsIStreamListener
>();
527 mLoadingDocTable
->Put(aURL
, aListener
);
533 nsBindingManager::GetLoadingDocListener(nsIURI
* aURL
)
535 if (!mLoadingDocTable
)
538 return mLoadingDocTable
->GetWeak(aURL
);
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.
558 binding
->PrototypeBinding()->DocURI()->GetPath(path
);
560 if (!strncmp(path
.get(), "/skin", 5))
561 binding
->MarkForDeath();
563 return PL_DHASH_NEXT
;
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
{
578 AntiRecursionData
* next
;
580 AntiRecursionData(nsIContent
* aElement
,
582 AntiRecursionData
* aNext
)
583 : element(aElement
), iid(aIID
), next(aNext
) {}
587 nsBindingManager::GetBindingImplementation(nsIContent
* aContent
, REFNSIID aIID
,
591 nsXBLBinding
*binding
= aContent
? aContent
->GetXBLBinding() : nullptr;
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
);
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
)) {
622 return NS_NOINTERFACE
;
626 AntiRecursionData
item(aContent
, aIID
, list
);
629 nsresult rv
= wrappedJS
->AggregatedQueryInterface(aIID
, aResult
);
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.
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
);
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
);
683 return NS_NOINTERFACE
;
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
;
700 nsXBLBinding
*binding
= content
->GetXBLBinding();
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
714 if (content
->IsRootOfNativeAnonymousSubtree()) {
715 break; // Deliberately cut off style inheritance here.
718 content
= content
->GetBindingParent();
721 // If "content" is non-null that means we cut off inheritance at some point
723 *aCutOffInheritance
= (content
!= nullptr);
725 // Null out the scoped root that we set repeatedly
726 aData
->mTreeMatchContext
.mScopedRoot
= nullptr;
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();
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
;
770 nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc
,
771 ElementDependentRuleProcessorData
* aData
)
773 if (!mBoundContentSet
) {
777 nsAutoPtr
<RuleProcessorSet
> set
;
778 mBoundContentSet
->EnumerateEntries(EnumRuleProcessors
, &set
);
782 WalkAllRulesData data
= { aFunc
, aData
};
783 set
->EnumerateEntries(EnumWalkAllRules
, &data
);
786 struct MediumFeaturesChangedData
{
787 nsPresContext
*mPresContext
;
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
;
806 nsBindingManager::MediumFeaturesChanged(nsPresContext
* aPresContext
,
809 *aRulesChanged
= false;
810 if (!mBoundContentSet
) {
814 nsAutoPtr
<RuleProcessorSet
> set
;
815 mBoundContentSet
->EnumerateEntries(EnumRuleProcessors
, &set
);
820 MediumFeaturesChangedData data
= { aPresContext
, aRulesChanged
};
821 set
->EnumerateEntries(EnumMediumFeaturesChanged
, &data
);
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
;
839 nsBindingManager::AppendAllSheets(nsTArray
<CSSStyleSheet
*>& aArray
)
841 if (mBoundContentSet
) {
842 mBoundContentSet
->EnumerateEntries(EnumAppendAllSheets
, &aArray
);
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.
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();
867 for (nsIContent
* currentChild
= aFirstNewContent
;
869 currentChild
= currentChild
->GetNextSibling()) {
870 aPoint
->InsertInsertedChildAt(currentChild
, insertionIndex
++);
875 nsBindingManager::ContentAppended(nsIDocument
* aDocument
,
876 nsIContent
* aContainer
,
877 nsIContent
* aFirstNewContent
,
878 int32_t aNewIndexInContainer
)
880 if (aNewIndexInContainer
== -1) {
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.
896 childrenEl
->MaybeSetupDefaultContent();
897 parent
= childrenEl
->GetParent();
902 nsXBLBinding
* binding
= GetBindingWithContent(parent
);
907 if (binding
->HasFilteredInsertionPoints()) {
908 // There are filtered insertion points involved, handle each child
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
913 int32_t currentIndex
= aNewIndexInContainer
;
914 for (nsIContent
* currentChild
= aFirstNewContent
; currentChild
;
915 currentChild
= currentChild
->GetNextSibling()) {
916 HandleChildInsertion(aContainer
, currentChild
,
917 currentIndex
++, true);
923 point
= binding
->GetDefaultInsertionPoint();
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
933 for (nsIContent
* child
= aFirstNewContent
; child
;
934 child
= child
->GetNextSibling()) {
935 point
->AppendInsertedChild(child
);
938 InsertAppendedContent(point
, aFirstNewContent
);
941 nsIContent
* newParent
= point
->GetParent();
942 if (newParent
== parent
) {
950 nsBindingManager::ContentInserted(nsIDocument
* aDocument
,
951 nsIContent
* aContainer
,
953 int32_t aIndexInContainer
)
955 if (aIndexInContainer
== -1) {
959 HandleChildInsertion(aContainer
, aChild
, aIndexInContainer
, false);
963 nsBindingManager::ContentRemoved(nsIDocument
* aDocument
,
964 nsIContent
* aContainer
,
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.
982 parent
= childrenEl
->GetParent();
986 nsXBLBinding
* binding
= GetBindingWithContent(parent
);
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
);
1001 point
= binding
->FindInsertionPointFor(aChild
);
1006 point
->RemoveInsertedChild(aChild
);
1008 nsIContent
* newParent
= point
->GetParent();
1009 if (newParent
== parent
) {
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
);
1030 nsBindingManager::DropDocumentReference()
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;
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
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
);
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
);
1076 nsBindingManager::BeginOutermostUpdate()
1078 mAttachedStackSizeOnOutermost
= mAttachedStack
.Length();
1082 nsBindingManager::EndOutermostUpdate()
1084 if (!mProcessingAttachedStack
) {
1085 ProcessAttachedQueue(mAttachedStackSizeOnOutermost
);
1086 mAttachedStackSizeOnOutermost
= 0;
1091 nsBindingManager::HandleChildInsertion(nsIContent
* aContainer
,
1093 uint32_t aIndexInContainer
,
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.
1112 childrenEl
->MaybeSetupDefaultContent();
1113 parent
= childrenEl
->GetParent();
1117 nsXBLBinding
* binding
= GetBindingWithContent(parent
);
1122 point
= binding
->FindInsertionPointFor(aChild
);
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();
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;
1145 point
->InsertInsertedChildAt(aChild
, index
);
1147 nsIContent
* newParent
= point
->GetParent();
1148 if (newParent
== parent
) {
1158 nsBindingManager::FindNestedInsertionPoint(nsIContent
* aContainer
,
1161 NS_PRECONDITION(aChild
->GetParent() == aContainer
,
1164 nsIContent
* parent
= aContainer
;
1165 if (aContainer
->IsActiveChildrenElement()) {
1166 if (static_cast<XBLChildrenElement
*>(aContainer
)->
1167 HasInsertedChildren()) {
1170 parent
= aContainer
->GetParent();
1174 nsXBLBinding
* binding
= GetBindingWithContent(parent
);
1179 XBLChildrenElement
* point
= binding
->FindInsertionPointFor(aChild
);
1184 nsIContent
* newParent
= point
->GetParent();
1185 if (newParent
== parent
) {
1195 nsBindingManager::FindNestedSingleInsertionPoint(nsIContent
* aContainer
,
1200 nsIContent
* parent
= aContainer
;
1201 if (aContainer
->IsActiveChildrenElement()) {
1202 if (static_cast<XBLChildrenElement
*>(aContainer
)->
1203 HasInsertedChildren()) {
1206 parent
= aContainer
->GetParent();
1210 nsXBLBinding
* binding
= GetBindingWithContent(parent
);
1215 if (binding
->HasFilteredInsertionPoints()) {
1220 XBLChildrenElement
* point
= binding
->GetDefaultInsertionPoint();
1225 nsIContent
* newParent
= point
->GetParent();
1226 if (newParent
== parent
) {