Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 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"
29 #include "nsXBLBinding.h"
30 #include "nsXBLPrototypeBinding.h"
31 #include "nsXBLDocumentInfo.h"
32 #include "mozilla/dom/XBLChildrenElement.h"
34 #include "nsIStyleRuleProcessor.h"
35 #include "nsRuleProcessorData.h"
36 #include "nsIWeakReference.h"
38 #include "nsWrapperCacheInlines.h"
39 #include "nsIXPConnect.h"
40 #include "nsDOMCID.h"
41 #include "nsIDOMScriptObjectFactory.h"
42 #include "nsIScriptGlobalObject.h"
43 #include "nsTHashtable.h"
45 #include "nsIScriptContext.h"
46 #include "xpcpublic.h"
47 #include "jswrapper.h"
48 #include "nsCxPusher.h"
50 #include "nsThreadUtils.h"
51 #include "mozilla/dom/NodeListBinding.h"
53 using namespace mozilla;
54 using namespace mozilla::dom;
56 // Implement our nsISupports methods
58 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
60 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager)
61 tmp->mDestroyed = true;
63 if (tmp->mBoundContentSet)
64 tmp->mBoundContentSet->Clear();
66 if (tmp->mDocumentTable)
67 tmp->mDocumentTable->Clear();
69 if (tmp->mLoadingDocTable)
70 tmp->mLoadingDocTable->Clear();
72 if (tmp->mWrapperTable) {
73 tmp->mWrapperTable->Clear();
74 tmp->mWrapperTable = nullptr;
75 }
77 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttachedStack)
79 if (tmp->mProcessAttachedQueueEvent) {
80 tmp->mProcessAttachedQueueEvent->Revoke();
81 }
82 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
85 static PLDHashOperator
86 DocumentInfoHashtableTraverser(nsIURI* key,
87 nsXBLDocumentInfo* di,
88 void* userArg)
89 {
90 nsCycleCollectionTraversalCallback *cb =
91 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
92 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mDocumentTable value");
93 cb->NoteXPCOMChild(di);
94 return PL_DHASH_NEXT;
95 }
97 static PLDHashOperator
98 LoadingDocHashtableTraverser(nsIURI* key,
99 nsIStreamListener* sl,
100 void* userArg)
101 {
102 nsCycleCollectionTraversalCallback *cb =
103 static_cast<nsCycleCollectionTraversalCallback*>(userArg);
104 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mLoadingDocTable value");
105 cb->NoteXPCOMChild(sl);
106 return PL_DHASH_NEXT;
107 }
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager)
110 // The hashes keyed on nsIContent are traversed from the nsIContent itself.
111 if (tmp->mDocumentTable)
112 tmp->mDocumentTable->EnumerateRead(&DocumentInfoHashtableTraverser, &cb);
113 if (tmp->mLoadingDocTable)
114 tmp->mLoadingDocTable->EnumerateRead(&LoadingDocHashtableTraverser, &cb);
115 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttachedStack)
116 // No need to traverse mProcessAttachedQueueEvent, since it'll just
117 // fire at some point or become revoke and drop its ref to us.
118 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
120 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager)
121 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
122 NS_INTERFACE_MAP_ENTRY(nsISupports)
123 NS_INTERFACE_MAP_END
125 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager)
126 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager)
128 // Constructors/Destructors
129 nsBindingManager::nsBindingManager(nsIDocument* aDocument)
130 : mProcessingAttachedStack(false),
131 mDestroyed(false),
132 mAttachedStackSizeOnOutermost(0),
133 mDocument(aDocument)
134 {
135 }
137 nsBindingManager::~nsBindingManager(void)
138 {
139 mDestroyed = true;
140 }
142 nsXBLBinding*
143 nsBindingManager::GetBindingWithContent(nsIContent* aContent)
144 {
145 nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr;
146 return binding ? binding->GetBindingWithContent() : nullptr;
147 }
149 void
150 nsBindingManager::AddBoundContent(nsIContent* aContent)
151 {
152 if (!mBoundContentSet) {
153 mBoundContentSet = new nsTHashtable<nsRefPtrHashKey<nsIContent> >;
154 }
155 mBoundContentSet->PutEntry(aContent);
156 }
158 void
159 nsBindingManager::RemoveBoundContent(nsIContent* aContent)
160 {
161 if (mBoundContentSet) {
162 mBoundContentSet->RemoveEntry(aContent);
163 }
165 // The death of the bindings means the death of the JS wrapper.
166 SetWrappedJS(aContent, nullptr);
167 }
169 nsIXPConnectWrappedJS*
170 nsBindingManager::GetWrappedJS(nsIContent* aContent)
171 {
172 if (!mWrapperTable) {
173 return nullptr;
174 }
176 if (!aContent || !aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
177 return nullptr;
178 }
180 return mWrapperTable->GetWeak(aContent);
181 }
183 nsresult
184 nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
185 {
186 if (mDestroyed) {
187 return NS_OK;
188 }
190 if (aWrappedJS) {
191 // lazily create the table, but only when adding elements
192 if (!mWrapperTable) {
193 mWrapperTable = new WrapperHashtable();
194 }
195 aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
197 NS_ASSERTION(aContent, "key must be non-null");
198 if (!aContent) return NS_ERROR_INVALID_ARG;
200 mWrapperTable->Put(aContent, aWrappedJS);
202 return NS_OK;
203 }
205 // no value, so remove the key from the table
206 if (mWrapperTable) {
207 mWrapperTable->Remove(aContent);
208 }
210 return NS_OK;
211 }
213 void
214 nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
215 nsIDocument* aOldDocument)
216 {
217 NS_PRECONDITION(aOldDocument != nullptr, "no old document");
219 if (mDestroyed)
220 return;
222 nsRefPtr<nsXBLBinding> binding = aContent->GetXBLBinding();
223 if (binding) {
224 binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement());
225 binding->ChangeDocument(aOldDocument, nullptr);
226 aContent->SetXBLBinding(nullptr, this);
227 }
229 // Clear out insertion parents and content lists.
230 aContent->SetXBLInsertionParent(nullptr);
231 }
233 nsIAtom*
234 nsBindingManager::ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID)
235 {
236 nsXBLBinding *binding = aContent->GetXBLBinding();
238 if (binding) {
239 nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
241 if (base) {
242 return base;
243 }
244 }
246 *aNameSpaceID = aContent->GetNameSpaceID();
247 return aContent->Tag();
248 }
250 nsresult
251 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
252 nsIDOMNodeList** aResult)
253 {
254 NS_IF_ADDREF(*aResult = GetAnonymousNodesFor(aContent));
255 return NS_OK;
256 }
258 nsINodeList*
259 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent)
260 {
261 nsXBLBinding* binding = GetBindingWithContent(aContent);
262 return binding ? binding->GetAnonymousNodeList() : nullptr;
263 }
265 nsresult
266 nsBindingManager::ClearBinding(nsIContent* aContent)
267 {
268 // Hold a ref to the binding so it won't die when we remove it from our table
269 nsRefPtr<nsXBLBinding> binding =
270 aContent ? aContent->GetXBLBinding() : nullptr;
272 if (!binding) {
273 return NS_OK;
274 }
276 // For now we can only handle removing a binding if it's the only one
277 NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE);
279 // Hold strong ref in case removing the binding tries to close the
280 // window or something.
281 // XXXbz should that be ownerdoc? Wouldn't we need a ref to the
282 // currentdoc too? What's the one that should be passed to
283 // ChangeDocument?
284 nsCOMPtr<nsIDocument> doc = aContent->OwnerDoc();
286 // Finally remove the binding...
287 // XXXbz this doesn't remove the implementation! Should fix! Until
288 // then we need the explicit UnhookEventHandlers here.
289 binding->UnhookEventHandlers();
290 binding->ChangeDocument(doc, nullptr);
291 aContent->SetXBLBinding(nullptr, this);
292 binding->MarkForDeath();
294 // ...and recreate its frames. We need to do this since the frames may have
295 // been removed and style may have changed due to the removal of the
296 // anonymous children.
297 // XXXbz this should be using the current doc (if any), not the owner doc.
298 nsIPresShell *presShell = doc->GetShell();
299 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
301 return presShell->RecreateFramesFor(aContent);;
302 }
304 nsresult
305 nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
306 nsIURI* aURL,
307 nsIPrincipal* aOriginPrincipal)
308 {
309 NS_PRECONDITION(aURL, "Must have a URI to load!");
311 // First we need to load our binding.
312 nsXBLService* xblService = nsXBLService::GetInstance();
313 if (!xblService)
314 return NS_ERROR_FAILURE;
316 // Load the binding doc.
317 nsRefPtr<nsXBLDocumentInfo> info;
318 xblService->LoadBindingDocumentInfo(nullptr, aBoundDoc, aURL,
319 aOriginPrincipal, true,
320 getter_AddRefs(info));
321 if (!info)
322 return NS_ERROR_FAILURE;
324 return NS_OK;
325 }
327 void
328 nsBindingManager::RemoveFromAttachedQueue(nsXBLBinding* aBinding)
329 {
330 // Don't remove items here as that could mess up an executing
331 // ProcessAttachedQueue. Instead, null the entry in the queue.
332 uint32_t index = mAttachedStack.IndexOf(aBinding);
333 if (index != mAttachedStack.NoIndex) {
334 mAttachedStack[index] = nullptr;
335 }
336 }
338 nsresult
339 nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
340 {
341 if (!mAttachedStack.AppendElement(aBinding))
342 return NS_ERROR_OUT_OF_MEMORY;
344 // If we're in the middle of processing our queue already, don't
345 // bother posting the event.
346 if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) {
347 PostProcessAttachedQueueEvent();
348 }
350 // Make sure that flushes will flush out the new items as needed.
351 mDocument->SetNeedStyleFlush();
353 return NS_OK;
355 }
357 void
358 nsBindingManager::PostProcessAttachedQueueEvent()
359 {
360 mProcessAttachedQueueEvent =
361 NS_NewRunnableMethod(this, &nsBindingManager::DoProcessAttachedQueue);
362 nsresult rv = NS_DispatchToCurrentThread(mProcessAttachedQueueEvent);
363 if (NS_SUCCEEDED(rv) && mDocument) {
364 mDocument->BlockOnload();
365 }
366 }
368 void
369 nsBindingManager::DoProcessAttachedQueue()
370 {
371 if (!mProcessingAttachedStack) {
372 ProcessAttachedQueue();
374 NS_ASSERTION(mAttachedStack.Length() == 0,
375 "Shouldn't have pending bindings!");
377 mProcessAttachedQueueEvent = nullptr;
378 } else {
379 // Someone's doing event processing from inside a constructor.
380 // They're evil, but we'll fight back! Just poll on them being
381 // done and repost the attached queue event.
382 PostProcessAttachedQueueEvent();
383 }
385 // No matter what, unblock onload for the event that's fired.
386 if (mDocument) {
387 // Hold a strong reference while calling UnblockOnload since that might
388 // run script.
389 nsCOMPtr<nsIDocument> doc = mDocument;
390 doc->UnblockOnload(true);
391 }
392 }
394 void
395 nsBindingManager::ProcessAttachedQueue(uint32_t aSkipSize)
396 {
397 if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
398 return;
400 mProcessingAttachedStack = true;
402 // Excute constructors. Do this from high index to low
403 while (mAttachedStack.Length() > aSkipSize) {
404 uint32_t lastItem = mAttachedStack.Length() - 1;
405 nsRefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem);
406 mAttachedStack.RemoveElementAt(lastItem);
407 if (binding) {
408 binding->ExecuteAttachedHandler();
409 }
410 }
412 // If NodeWillBeDestroyed has run we don't want to clobber
413 // mProcessingAttachedStack set there.
414 if (mDocument) {
415 mProcessingAttachedStack = false;
416 }
418 NS_ASSERTION(mAttachedStack.Length() == aSkipSize, "How did we get here?");
420 mAttachedStack.Compact();
421 }
423 // Keep bindings and bound elements alive while executing detached handlers.
424 struct BindingTableReadClosure
425 {
426 nsCOMArray<nsIContent> mBoundElements;
427 nsBindingList mBindings;
428 };
430 static PLDHashOperator
431 AccumulateBindingsToDetach(nsRefPtrHashKey<nsIContent> *aKey,
432 void* aClosure)
433 {
434 nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
435 BindingTableReadClosure* closure =
436 static_cast<BindingTableReadClosure*>(aClosure);
437 if (binding && closure->mBindings.AppendElement(binding)) {
438 if (!closure->mBoundElements.AppendObject(binding->GetBoundElement())) {
439 closure->mBindings.RemoveElementAt(closure->mBindings.Length() - 1);
440 }
441 }
442 return PL_DHASH_NEXT;
443 }
445 void
446 nsBindingManager::ExecuteDetachedHandlers()
447 {
448 // Walk our hashtable of bindings.
449 if (mBoundContentSet) {
450 BindingTableReadClosure closure;
451 mBoundContentSet->EnumerateEntries(AccumulateBindingsToDetach, &closure);
452 uint32_t i, count = closure.mBindings.Length();
453 for (i = 0; i < count; ++i) {
454 closure.mBindings[i]->ExecuteDetachedHandler();
455 }
456 }
457 }
459 nsresult
460 nsBindingManager::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
461 {
462 NS_PRECONDITION(aDocumentInfo, "Must have a non-null documentinfo!");
464 if (!mDocumentTable) {
465 mDocumentTable = new nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>(16);
466 }
468 mDocumentTable->Put(aDocumentInfo->DocumentURI(), aDocumentInfo);
470 return NS_OK;
471 }
473 void
474 nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
475 {
476 if (mDocumentTable) {
477 mDocumentTable->Remove(aDocumentInfo->DocumentURI());
478 }
479 }
481 nsXBLDocumentInfo*
482 nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL)
483 {
484 if (!mDocumentTable)
485 return nullptr;
487 return mDocumentTable->GetWeak(aURL);
488 }
490 nsresult
491 nsBindingManager::PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener)
492 {
493 NS_PRECONDITION(aListener, "Must have a non-null listener!");
495 if (!mLoadingDocTable) {
496 mLoadingDocTable = new nsInterfaceHashtable<nsURIHashKey,nsIStreamListener>(16);
497 }
498 mLoadingDocTable->Put(aURL, aListener);
500 return NS_OK;
501 }
503 nsIStreamListener*
504 nsBindingManager::GetLoadingDocListener(nsIURI* aURL)
505 {
506 if (!mLoadingDocTable)
507 return nullptr;
509 return mLoadingDocTable->GetWeak(aURL);
510 }
512 void
513 nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL)
514 {
515 if (mLoadingDocTable) {
516 mLoadingDocTable->Remove(aURL);
517 }
518 }
520 static PLDHashOperator
521 MarkForDeath(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
522 {
523 nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
525 if (binding->MarkedForDeath())
526 return PL_DHASH_NEXT; // Already marked for death.
528 nsAutoCString path;
529 binding->PrototypeBinding()->DocURI()->GetPath(path);
531 if (!strncmp(path.get(), "/skin", 5))
532 binding->MarkForDeath();
534 return PL_DHASH_NEXT;
535 }
537 void
538 nsBindingManager::FlushSkinBindings()
539 {
540 if (mBoundContentSet) {
541 mBoundContentSet->EnumerateEntries(MarkForDeath, nullptr);
542 }
543 }
545 // Used below to protect from recurring in QI calls through XPConnect.
546 struct AntiRecursionData {
547 nsIContent* element;
548 REFNSIID iid;
549 AntiRecursionData* next;
551 AntiRecursionData(nsIContent* aElement,
552 REFNSIID aIID,
553 AntiRecursionData* aNext)
554 : element(aElement), iid(aIID), next(aNext) {}
555 };
557 nsresult
558 nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
559 void** aResult)
560 {
561 *aResult = nullptr;
562 nsXBLBinding *binding = aContent ? aContent->GetXBLBinding() : nullptr;
563 if (binding) {
564 // The binding should not be asked for nsISupports
565 NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports");
566 if (binding->ImplementsInterface(aIID)) {
567 nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent);
569 if (wrappedJS) {
570 // Protect from recurring in QI calls through XPConnect.
571 // This can happen when a second binding is being resolved.
572 // At that point a wrappedJS exists, but it doesn't yet know about
573 // the iid we are asking for. So, without this protection,
574 // AggregatedQueryInterface would end up recurring back into itself
575 // through this code.
576 //
577 // With this protection, when we detect the recursion we return
578 // NS_NOINTERFACE in the inner call. The outer call will then fall
579 // through (see below) and build a new chained wrappedJS for the iid.
580 //
581 // We're careful to not assume that only one direct nesting can occur
582 // because there is a call into JS in the middle and we can't assume
583 // that this code won't be reached by some more complex nesting path.
584 //
585 // NOTE: We *assume* this is single threaded, so we can use a
586 // static linked list to do the check.
588 static AntiRecursionData* list = nullptr;
590 for (AntiRecursionData* p = list; p; p = p->next) {
591 if (p->element == aContent && p->iid.Equals(aIID)) {
592 *aResult = nullptr;
593 return NS_NOINTERFACE;
594 }
595 }
597 AntiRecursionData item(aContent, aIID, list);
598 list = &item;
600 nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
602 list = item.next;
604 if (*aResult)
605 return rv;
607 // No result was found, so this must be another XBL interface.
608 // Fall through to create a new wrapper.
609 }
611 // We have never made a wrapper for this implementation.
612 // Create an XPC wrapper for the script object and hand it back.
614 nsIDocument* doc = aContent->OwnerDoc();
616 nsCOMPtr<nsIScriptGlobalObject> global =
617 do_QueryInterface(doc->GetWindow());
618 if (!global)
619 return NS_NOINTERFACE;
621 nsIScriptContext *context = global->GetContext();
622 if (!context)
623 return NS_NOINTERFACE;
625 AutoPushJSContext cx(context->GetNativeContext());
626 if (!cx)
627 return NS_NOINTERFACE;
629 nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
631 JS::Rooted<JSObject*> jsobj(cx, aContent->GetWrapper());
632 NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE);
634 // If we're using an XBL scope, we need to use the Xray view to the bound
635 // content in order to view the full array of methods defined in the
636 // binding, some of which may not be exposed on the prototype of
637 // untrusted content.
638 //
639 // If there's no separate XBL scope, or if the reflector itself lives in
640 // the XBL scope, we'll end up with the global of the reflector, and this
641 // will all be a no-op.
642 JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, jsobj));
643 NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED);
644 JSAutoCompartment ac(cx, xblScope);
645 bool ok = JS_WrapObject(cx, &jsobj);
646 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
647 MOZ_ASSERT_IF(js::IsWrapper(jsobj), xpc::IsXrayWrapper(jsobj));
649 nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, cx,
650 jsobj, aIID, aResult);
651 if (NS_FAILED(rv))
652 return rv;
654 // We successfully created a wrapper. We will own this wrapper for as long as the binding remains
655 // alive. At the time the binding is cleared out of the bindingManager, we will remove the wrapper
656 // from the bindingManager as well.
657 nsISupports* supp = static_cast<nsISupports*>(*aResult);
658 wrappedJS = do_QueryInterface(supp);
659 SetWrappedJS(aContent, wrappedJS);
661 return rv;
662 }
663 }
665 *aResult = nullptr;
666 return NS_NOINTERFACE;
667 }
669 nsresult
670 nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
671 ElementDependentRuleProcessorData* aData,
672 bool* aCutOffInheritance)
673 {
674 *aCutOffInheritance = false;
676 NS_ASSERTION(aData->mElement, "How did that happen?");
678 // Walk the binding scope chain, starting with the binding attached to our
679 // content, up till we run out of scopes or we get cut off.
680 nsIContent *content = aData->mElement;
682 do {
683 nsXBLBinding *binding = content->GetXBLBinding();
684 if (binding) {
685 aData->mTreeMatchContext.mScopedRoot = content;
686 binding->WalkRules(aFunc, aData);
687 // If we're not looking at our original content, allow the binding to cut
688 // off style inheritance
689 if (content != aData->mElement) {
690 if (!binding->InheritsStyle()) {
691 // Go no further; we're not inheriting style from anything above here
692 break;
693 }
694 }
695 }
697 if (content->IsRootOfNativeAnonymousSubtree()) {
698 break; // Deliberately cut off style inheritance here.
699 }
701 content = content->GetBindingParent();
702 } while (content);
704 // If "content" is non-null that means we cut off inheritance at some point
705 // in the loop.
706 *aCutOffInheritance = (content != nullptr);
708 // Null out the scoped root that we set repeatedly
709 aData->mTreeMatchContext.mScopedRoot = nullptr;
711 return NS_OK;
712 }
714 typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
716 static PLDHashOperator
717 EnumRuleProcessors(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
718 {
719 nsIContent *boundContent = aKey->GetKey();
720 nsAutoPtr<RuleProcessorSet> *set = static_cast<nsAutoPtr<RuleProcessorSet>*>(aClosure);
721 for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
722 binding = binding->GetBaseBinding()) {
723 nsIStyleRuleProcessor *ruleProc =
724 binding->PrototypeBinding()->GetRuleProcessor();
725 if (ruleProc) {
726 if (!(*set)) {
727 *set = new RuleProcessorSet;
728 }
729 (*set)->PutEntry(ruleProc);
730 }
731 }
732 return PL_DHASH_NEXT;
733 }
735 struct WalkAllRulesData {
736 nsIStyleRuleProcessor::EnumFunc mFunc;
737 ElementDependentRuleProcessorData* mData;
738 };
740 static PLDHashOperator
741 EnumWalkAllRules(nsPtrHashKey<nsIStyleRuleProcessor> *aKey, void* aClosure)
742 {
743 nsIStyleRuleProcessor *ruleProcessor = aKey->GetKey();
745 WalkAllRulesData *data = static_cast<WalkAllRulesData*>(aClosure);
747 (*(data->mFunc))(ruleProcessor, data->mData);
749 return PL_DHASH_NEXT;
750 }
752 void
753 nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
754 ElementDependentRuleProcessorData* aData)
755 {
756 if (!mBoundContentSet) {
757 return;
758 }
760 nsAutoPtr<RuleProcessorSet> set;
761 mBoundContentSet->EnumerateEntries(EnumRuleProcessors, &set);
762 if (!set)
763 return;
765 WalkAllRulesData data = { aFunc, aData };
766 set->EnumerateEntries(EnumWalkAllRules, &data);
767 }
769 struct MediumFeaturesChangedData {
770 nsPresContext *mPresContext;
771 bool *mRulesChanged;
772 };
774 static PLDHashOperator
775 EnumMediumFeaturesChanged(nsPtrHashKey<nsIStyleRuleProcessor> *aKey, void* aClosure)
776 {
777 nsIStyleRuleProcessor *ruleProcessor = aKey->GetKey();
779 MediumFeaturesChangedData *data =
780 static_cast<MediumFeaturesChangedData*>(aClosure);
782 bool thisChanged = ruleProcessor->MediumFeaturesChanged(data->mPresContext);
783 *data->mRulesChanged = *data->mRulesChanged || thisChanged;
785 return PL_DHASH_NEXT;
786 }
788 nsresult
789 nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
790 bool* aRulesChanged)
791 {
792 *aRulesChanged = false;
793 if (!mBoundContentSet) {
794 return NS_OK;
795 }
797 nsAutoPtr<RuleProcessorSet> set;
798 mBoundContentSet->EnumerateEntries(EnumRuleProcessors, &set);
799 if (!set) {
800 return NS_OK;
801 }
803 MediumFeaturesChangedData data = { aPresContext, aRulesChanged };
804 set->EnumerateEntries(EnumMediumFeaturesChanged, &data);
805 return NS_OK;
806 }
808 static PLDHashOperator
809 EnumAppendAllSheets(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
810 {
811 nsIContent *boundContent = aKey->GetKey();
812 nsTArray<nsCSSStyleSheet*>* array =
813 static_cast<nsTArray<nsCSSStyleSheet*>*>(aClosure);
814 for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
815 binding = binding->GetBaseBinding()) {
816 nsXBLPrototypeResources::sheet_array_type* sheets =
817 binding->PrototypeBinding()->GetStyleSheets();
818 if (sheets) {
819 // Copy from nsTArray<nsRefPtr<nsCSSStyleSheet> > to
820 // nsTArray<nsCSSStyleSheet*>.
821 array->AppendElements(*sheets);
822 }
823 }
824 return PL_DHASH_NEXT;
825 }
827 void
828 nsBindingManager::AppendAllSheets(nsTArray<nsCSSStyleSheet*>& aArray)
829 {
830 if (mBoundContentSet) {
831 mBoundContentSet->EnumerateEntries(EnumAppendAllSheets, &aArray);
832 }
833 }
835 static void
836 InsertAppendedContent(XBLChildrenElement* aPoint,
837 nsIContent* aFirstNewContent)
838 {
839 uint32_t insertionIndex;
840 if (nsIContent* prevSibling = aFirstNewContent->GetPreviousSibling()) {
841 // If we have a previous sibling, then it must already be in aPoint. Find
842 // it and insert after it.
843 insertionIndex = aPoint->IndexOfInsertedChild(prevSibling);
844 MOZ_ASSERT(insertionIndex != aPoint->NoIndex);
846 // Our insertion index is one after our previous sibling's index.
847 ++insertionIndex;
848 } else {
849 // Otherwise, we append.
850 // TODO This is wrong for nested insertion points. In that case, we need to
851 // keep track of the right index to insert into.
852 insertionIndex = aPoint->mInsertedChildren.Length();
853 }
855 // Do the inserting.
856 for (nsIContent* currentChild = aFirstNewContent;
857 currentChild;
858 currentChild = currentChild->GetNextSibling()) {
859 aPoint->InsertInsertedChildAt(currentChild, insertionIndex++);
860 }
861 }
863 void
864 nsBindingManager::ContentAppended(nsIDocument* aDocument,
865 nsIContent* aContainer,
866 nsIContent* aFirstNewContent,
867 int32_t aNewIndexInContainer)
868 {
869 if (aNewIndexInContainer == -1) {
870 return;
871 }
873 // Try to find insertion points for all the new kids.
874 XBLChildrenElement* point = nullptr;
875 nsIContent* parent = aContainer;
876 bool first = true;
877 do {
878 nsXBLBinding* binding = GetBindingWithContent(parent);
879 if (!binding) {
880 break;
881 }
883 if (binding->HasFilteredInsertionPoints()) {
884 // There are filtered insertion points involved, handle each child
885 // separately.
886 // We could optimize this in the case when we've nested a few levels
887 // deep already, without hitting bindings that have filtered insertion
888 // points.
889 int32_t currentIndex = aNewIndexInContainer;
890 for (nsIContent* currentChild = aFirstNewContent; currentChild;
891 currentChild = currentChild->GetNextSibling()) {
892 HandleChildInsertion(aContainer, currentChild,
893 currentIndex++, true);
894 }
896 return;
897 }
899 point = binding->GetDefaultInsertionPoint();
900 if (!point) {
901 break;
902 }
904 // Even though we're in ContentAppended, nested insertion points force us
905 // to deal with this append as an insertion except in the outermost
906 // binding.
907 if (first) {
908 first = false;
909 for (nsIContent* child = aFirstNewContent; child;
910 child = child->GetNextSibling()) {
911 point->AppendInsertedChild(child);
912 }
913 } else {
914 InsertAppendedContent(point, aFirstNewContent);
915 }
917 nsIContent* newParent = point->GetParent();
918 if (newParent == parent) {
919 break;
920 }
921 parent = newParent;
922 } while (parent);
923 }
925 void
926 nsBindingManager::ContentInserted(nsIDocument* aDocument,
927 nsIContent* aContainer,
928 nsIContent* aChild,
929 int32_t aIndexInContainer)
930 {
931 if (aIndexInContainer == -1) {
932 return;
933 }
935 HandleChildInsertion(aContainer, aChild, aIndexInContainer, false);
936 }
938 void
939 nsBindingManager::ContentRemoved(nsIDocument* aDocument,
940 nsIContent* aContainer,
941 nsIContent* aChild,
942 int32_t aIndexInContainer,
943 nsIContent* aPreviousSibling)
944 {
945 aChild->SetXBLInsertionParent(nullptr);
947 XBLChildrenElement* point = nullptr;
948 nsIContent* parent = aContainer;
949 do {
950 nsXBLBinding* binding = GetBindingWithContent(parent);
951 if (!binding) {
952 // If aChild is XBL content, it might have <xbl:children> elements
953 // somewhere under it. We need to inform those elements that they're no
954 // longer in the tree so they can tell their distributed children that
955 // they're no longer distributed under them.
956 // XXX This is wrong. We need to do far more work to update the parent
957 // binding's list of insertion points and to get the new insertion parent
958 // for the newly-distributed children correct.
959 if (aChild->GetBindingParent()) {
960 ClearInsertionPointsRecursively(aChild);
961 }
962 return;
963 }
965 point = binding->FindInsertionPointFor(aChild);
966 if (!point) {
967 break;
968 }
970 point->RemoveInsertedChild(aChild);
972 nsIContent* newParent = point->GetParent();
973 if (newParent == parent) {
974 break;
975 }
976 parent = newParent;
977 } while (parent);
978 }
980 void
981 nsBindingManager::ClearInsertionPointsRecursively(nsIContent* aContent)
982 {
983 if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
984 static_cast<XBLChildrenElement*>(aContent)->ClearInsertedChildren();
985 }
987 for (nsIContent* child = aContent->GetFirstChild(); child;
988 child = child->GetNextSibling()) {
989 ClearInsertionPointsRecursively(child);
990 }
991 }
993 void
994 nsBindingManager::DropDocumentReference()
995 {
996 mDestroyed = true;
998 // Make sure to not run any more XBL constructors
999 mProcessingAttachedStack = true;
1000 if (mProcessAttachedQueueEvent) {
1001 mProcessAttachedQueueEvent->Revoke();
1002 }
1004 if (mBoundContentSet) {
1005 mBoundContentSet->Clear();
1006 }
1008 mDocument = nullptr;
1009 }
1011 void
1012 nsBindingManager::Traverse(nsIContent *aContent,
1013 nsCycleCollectionTraversalCallback &cb)
1014 {
1015 if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
1016 !aContent->IsElement()) {
1017 // Don't traverse if content is not in this binding manager.
1018 // We also don't traverse non-elements because there should not
1019 // be bindings (checking the flag alone is not sufficient because
1020 // the flag is also set on children of insertion points that may be
1021 // non-elements).
1022 return;
1023 }
1025 if (mBoundContentSet && mBoundContentSet->Contains(aContent)) {
1026 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBoundContentSet entry");
1027 cb.NoteXPCOMChild(aContent);
1028 }
1030 nsIXPConnectWrappedJS *value = GetWrappedJS(aContent);
1031 if (value) {
1032 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
1033 cb.NoteXPCOMChild(aContent);
1034 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
1035 cb.NoteXPCOMChild(value);
1036 }
1037 }
1039 void
1040 nsBindingManager::BeginOutermostUpdate()
1041 {
1042 mAttachedStackSizeOnOutermost = mAttachedStack.Length();
1043 }
1045 void
1046 nsBindingManager::EndOutermostUpdate()
1047 {
1048 if (!mProcessingAttachedStack) {
1049 ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
1050 mAttachedStackSizeOnOutermost = 0;
1051 }
1052 }
1054 void
1055 nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
1056 nsIContent* aChild,
1057 uint32_t aIndexInContainer,
1058 bool aAppend)
1059 {
1060 NS_PRECONDITION(aChild, "Must have child");
1061 NS_PRECONDITION(!aContainer ||
1062 uint32_t(aContainer->IndexOf(aChild)) == aIndexInContainer,
1063 "Child not at the right index?");
1065 XBLChildrenElement* point = nullptr;
1066 nsIContent* parent = aContainer;
1067 while (parent) {
1068 nsXBLBinding* binding = GetBindingWithContent(parent);
1069 if (!binding) {
1070 break;
1071 }
1073 point = binding->FindInsertionPointFor(aChild);
1074 if (!point) {
1075 break;
1076 }
1078 // Insert the child into the proper insertion point.
1079 // TODO If there were multiple insertion points, this approximation can be
1080 // wrong. We need to re-run the distribution algorithm. In the meantime,
1081 // this should work well enough.
1082 uint32_t index = aAppend ? point->mInsertedChildren.Length() : 0;
1083 for (nsIContent* currentSibling = aChild->GetPreviousSibling();
1084 currentSibling;
1085 currentSibling = currentSibling->GetPreviousSibling()) {
1086 // If we find one of our previous siblings in the insertion point, the
1087 // index following it is the correct insertion point. Otherwise, we guess
1088 // based on whether we're appending or inserting.
1089 uint32_t pointIndex = point->IndexOfInsertedChild(currentSibling);
1090 if (pointIndex != point->NoIndex) {
1091 index = pointIndex + 1;
1092 break;
1093 }
1094 }
1096 point->InsertInsertedChildAt(aChild, index);
1098 nsIContent* newParent = point->GetParent();
1099 if (newParent == parent) {
1100 break;
1101 }
1103 parent = newParent;
1104 }
1105 }
1108 nsIContent*
1109 nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
1110 nsIContent* aChild)
1111 {
1112 NS_PRECONDITION(aChild->GetParent() == aContainer,
1113 "Wrong container");
1115 nsIContent* parent = aContainer;
1116 if (aContainer->IsActiveChildrenElement()) {
1117 if (static_cast<XBLChildrenElement*>(aContainer)->
1118 HasInsertedChildren()) {
1119 return nullptr;
1120 }
1121 parent = aContainer->GetParent();
1122 }
1124 while (parent) {
1125 nsXBLBinding* binding = GetBindingWithContent(parent);
1126 if (!binding) {
1127 break;
1128 }
1130 XBLChildrenElement* point = binding->FindInsertionPointFor(aChild);
1131 if (!point) {
1132 return nullptr;
1133 }
1135 nsIContent* newParent = point->GetParent();
1136 if (newParent == parent) {
1137 break;
1138 }
1139 parent = newParent;
1140 }
1142 return parent;
1143 }
1145 nsIContent*
1146 nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
1147 bool* aMulti)
1148 {
1149 *aMulti = false;
1151 nsIContent* parent = aContainer;
1152 if (aContainer->IsActiveChildrenElement()) {
1153 if (static_cast<XBLChildrenElement*>(aContainer)->
1154 HasInsertedChildren()) {
1155 return nullptr;
1156 }
1157 parent = aContainer->GetParent();
1158 }
1160 while(parent) {
1161 nsXBLBinding* binding = GetBindingWithContent(parent);
1162 if (!binding) {
1163 break;
1164 }
1166 if (binding->HasFilteredInsertionPoints()) {
1167 *aMulti = true;
1168 return nullptr;
1169 }
1171 XBLChildrenElement* point = binding->GetDefaultInsertionPoint();
1172 if (!point) {
1173 return nullptr;
1174 }
1176 nsIContent* newParent = point->GetParent();
1177 if (newParent == parent) {
1178 break;
1179 }
1180 parent = newParent;
1181 }
1183 return parent;
1184 }