dom/xbl/nsBindingManager.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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();
  1004   if (mBoundContentSet) {
  1005     mBoundContentSet->Clear();
  1008   mDocument = nullptr;
  1011 void
  1012 nsBindingManager::Traverse(nsIContent *aContent,
  1013                            nsCycleCollectionTraversalCallback &cb)
  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;
  1025   if (mBoundContentSet && mBoundContentSet->Contains(aContent)) {
  1026     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBoundContentSet entry");
  1027     cb.NoteXPCOMChild(aContent);
  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);
  1039 void
  1040 nsBindingManager::BeginOutermostUpdate()
  1042   mAttachedStackSizeOnOutermost = mAttachedStack.Length();
  1045 void
  1046 nsBindingManager::EndOutermostUpdate()
  1048   if (!mProcessingAttachedStack) {
  1049     ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
  1050     mAttachedStackSizeOnOutermost = 0;
  1054 void
  1055 nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
  1056                                        nsIContent* aChild,
  1057                                        uint32_t aIndexInContainer,
  1058                                        bool aAppend)
  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;
  1073     point = binding->FindInsertionPointFor(aChild);
  1074     if (!point) {
  1075       break;
  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;
  1096     point->InsertInsertedChildAt(aChild, index);
  1098     nsIContent* newParent = point->GetParent();
  1099     if (newParent == parent) {
  1100       break;
  1103     parent = newParent;
  1108 nsIContent*
  1109 nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
  1110                                            nsIContent* aChild)
  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;
  1121     parent = aContainer->GetParent();
  1124   while (parent) {
  1125     nsXBLBinding* binding = GetBindingWithContent(parent);
  1126     if (!binding) {
  1127       break;
  1130     XBLChildrenElement* point = binding->FindInsertionPointFor(aChild);
  1131     if (!point) {
  1132       return nullptr;
  1135     nsIContent* newParent = point->GetParent();
  1136     if (newParent == parent) {
  1137       break;
  1139     parent = newParent;
  1142   return parent;
  1145 nsIContent*
  1146 nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
  1147                                                  bool* aMulti)
  1149   *aMulti = false;
  1151   nsIContent* parent = aContainer;
  1152   if (aContainer->IsActiveChildrenElement()) {
  1153     if (static_cast<XBLChildrenElement*>(aContainer)->
  1154           HasInsertedChildren()) {
  1155       return nullptr;
  1157     parent = aContainer->GetParent();
  1160   while(parent) {
  1161     nsXBLBinding* binding = GetBindingWithContent(parent);
  1162     if (!binding) {
  1163       break;
  1166     if (binding->HasFilteredInsertionPoints()) {
  1167       *aMulti = true;
  1168       return nullptr;
  1171     XBLChildrenElement* point = binding->GetDefaultInsertionPoint();
  1172     if (!point) {
  1173       return nullptr;
  1176     nsIContent* newParent = point->GetParent();
  1177     if (newParent == parent) {
  1178       break;
  1180     parent = newParent;
  1183   return parent;

mercurial