dom/xbl/nsBindingManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/xbl/nsBindingManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1184 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=79: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsBindingManager.h"
    1.11 +
    1.12 +#include "nsCOMPtr.h"
    1.13 +#include "nsXBLService.h"
    1.14 +#include "nsIInputStream.h"
    1.15 +#include "nsIURI.h"
    1.16 +#include "nsIURL.h"
    1.17 +#include "nsIChannel.h"
    1.18 +#include "nsXPIDLString.h"
    1.19 +#include "nsNetUtil.h"
    1.20 +#include "plstr.h"
    1.21 +#include "nsIContent.h"
    1.22 +#include "nsIDOMElement.h"
    1.23 +#include "nsIDocument.h"
    1.24 +#include "nsContentUtils.h"
    1.25 +#include "nsIPresShell.h"
    1.26 +#include "nsIXMLContentSink.h"
    1.27 +#include "nsContentCID.h"
    1.28 +#include "mozilla/dom/XMLDocument.h"
    1.29 +#include "nsIStreamListener.h"
    1.30 +#include "ChildIterator.h"
    1.31 +
    1.32 +#include "nsXBLBinding.h"
    1.33 +#include "nsXBLPrototypeBinding.h"
    1.34 +#include "nsXBLDocumentInfo.h"
    1.35 +#include "mozilla/dom/XBLChildrenElement.h"
    1.36 +
    1.37 +#include "nsIStyleRuleProcessor.h"
    1.38 +#include "nsRuleProcessorData.h"
    1.39 +#include "nsIWeakReference.h"
    1.40 +
    1.41 +#include "nsWrapperCacheInlines.h"
    1.42 +#include "nsIXPConnect.h"
    1.43 +#include "nsDOMCID.h"
    1.44 +#include "nsIDOMScriptObjectFactory.h"
    1.45 +#include "nsIScriptGlobalObject.h"
    1.46 +#include "nsTHashtable.h"
    1.47 +
    1.48 +#include "nsIScriptContext.h"
    1.49 +#include "xpcpublic.h"
    1.50 +#include "jswrapper.h"
    1.51 +#include "nsCxPusher.h"
    1.52 +
    1.53 +#include "nsThreadUtils.h"
    1.54 +#include "mozilla/dom/NodeListBinding.h"
    1.55 +
    1.56 +using namespace mozilla;
    1.57 +using namespace mozilla::dom;
    1.58 +
    1.59 +// Implement our nsISupports methods
    1.60 +
    1.61 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
    1.62 +
    1.63 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager)
    1.64 +  tmp->mDestroyed = true;
    1.65 +
    1.66 +  if (tmp->mBoundContentSet)
    1.67 +    tmp->mBoundContentSet->Clear();
    1.68 +
    1.69 +  if (tmp->mDocumentTable)
    1.70 +    tmp->mDocumentTable->Clear();
    1.71 +
    1.72 +  if (tmp->mLoadingDocTable)
    1.73 +    tmp->mLoadingDocTable->Clear();
    1.74 +
    1.75 +  if (tmp->mWrapperTable) {
    1.76 +    tmp->mWrapperTable->Clear();
    1.77 +    tmp->mWrapperTable = nullptr;
    1.78 +  }
    1.79 +
    1.80 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttachedStack)
    1.81 +
    1.82 +  if (tmp->mProcessAttachedQueueEvent) {
    1.83 +    tmp->mProcessAttachedQueueEvent->Revoke();
    1.84 +  }
    1.85 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1.86 +
    1.87 +
    1.88 +static PLDHashOperator
    1.89 +DocumentInfoHashtableTraverser(nsIURI* key,
    1.90 +                               nsXBLDocumentInfo* di,
    1.91 +                               void* userArg)
    1.92 +{
    1.93 +  nsCycleCollectionTraversalCallback *cb =
    1.94 +    static_cast<nsCycleCollectionTraversalCallback*>(userArg);
    1.95 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mDocumentTable value");
    1.96 +  cb->NoteXPCOMChild(di);
    1.97 +  return PL_DHASH_NEXT;
    1.98 +}
    1.99 +
   1.100 +static PLDHashOperator
   1.101 +LoadingDocHashtableTraverser(nsIURI* key,
   1.102 +                             nsIStreamListener* sl,
   1.103 +                             void* userArg)
   1.104 +{
   1.105 +  nsCycleCollectionTraversalCallback *cb =
   1.106 +    static_cast<nsCycleCollectionTraversalCallback*>(userArg);
   1.107 +  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mLoadingDocTable value");
   1.108 +  cb->NoteXPCOMChild(sl);
   1.109 +  return PL_DHASH_NEXT;
   1.110 +}
   1.111 +
   1.112 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager)
   1.113 +  // The hashes keyed on nsIContent are traversed from the nsIContent itself.
   1.114 +  if (tmp->mDocumentTable)
   1.115 +      tmp->mDocumentTable->EnumerateRead(&DocumentInfoHashtableTraverser, &cb);
   1.116 +  if (tmp->mLoadingDocTable)
   1.117 +      tmp->mLoadingDocTable->EnumerateRead(&LoadingDocHashtableTraverser, &cb);
   1.118 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttachedStack)
   1.119 +  // No need to traverse mProcessAttachedQueueEvent, since it'll just
   1.120 +  // fire at some point or become revoke and drop its ref to us.
   1.121 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.122 +
   1.123 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager)
   1.124 +  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   1.125 +  NS_INTERFACE_MAP_ENTRY(nsISupports)
   1.126 +NS_INTERFACE_MAP_END
   1.127 +
   1.128 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager)
   1.129 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager)
   1.130 +
   1.131 +// Constructors/Destructors
   1.132 +nsBindingManager::nsBindingManager(nsIDocument* aDocument)
   1.133 +  : mProcessingAttachedStack(false),
   1.134 +    mDestroyed(false),
   1.135 +    mAttachedStackSizeOnOutermost(0),
   1.136 +    mDocument(aDocument)
   1.137 +{
   1.138 +}
   1.139 +
   1.140 +nsBindingManager::~nsBindingManager(void)
   1.141 +{
   1.142 +  mDestroyed = true;
   1.143 +}
   1.144 +
   1.145 +nsXBLBinding*
   1.146 +nsBindingManager::GetBindingWithContent(nsIContent* aContent)
   1.147 +{
   1.148 +  nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr;
   1.149 +  return binding ? binding->GetBindingWithContent() : nullptr;
   1.150 +}
   1.151 +
   1.152 +void
   1.153 +nsBindingManager::AddBoundContent(nsIContent* aContent)
   1.154 +{
   1.155 +  if (!mBoundContentSet) {
   1.156 +    mBoundContentSet = new nsTHashtable<nsRefPtrHashKey<nsIContent> >;
   1.157 +  }
   1.158 +  mBoundContentSet->PutEntry(aContent);
   1.159 +}
   1.160 +
   1.161 +void
   1.162 +nsBindingManager::RemoveBoundContent(nsIContent* aContent)
   1.163 +{
   1.164 +  if (mBoundContentSet) {
   1.165 +    mBoundContentSet->RemoveEntry(aContent);
   1.166 +  }
   1.167 +
   1.168 +  // The death of the bindings means the death of the JS wrapper.
   1.169 +  SetWrappedJS(aContent, nullptr);
   1.170 +}
   1.171 +
   1.172 +nsIXPConnectWrappedJS*
   1.173 +nsBindingManager::GetWrappedJS(nsIContent* aContent)
   1.174 +{
   1.175 +  if (!mWrapperTable) {
   1.176 +    return nullptr;
   1.177 +  }
   1.178 +
   1.179 +  if (!aContent || !aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
   1.180 +    return nullptr;
   1.181 +  }
   1.182 +
   1.183 +  return mWrapperTable->GetWeak(aContent);
   1.184 +}
   1.185 +
   1.186 +nsresult
   1.187 +nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
   1.188 +{
   1.189 +  if (mDestroyed) {
   1.190 +    return NS_OK;
   1.191 +  }
   1.192 +
   1.193 +  if (aWrappedJS) {
   1.194 +    // lazily create the table, but only when adding elements
   1.195 +    if (!mWrapperTable) {
   1.196 +      mWrapperTable = new WrapperHashtable();
   1.197 +    }
   1.198 +    aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
   1.199 +
   1.200 +    NS_ASSERTION(aContent, "key must be non-null");
   1.201 +    if (!aContent) return NS_ERROR_INVALID_ARG;
   1.202 +
   1.203 +    mWrapperTable->Put(aContent, aWrappedJS);
   1.204 +
   1.205 +    return NS_OK;
   1.206 +  }
   1.207 +
   1.208 +  // no value, so remove the key from the table
   1.209 +  if (mWrapperTable) {
   1.210 +    mWrapperTable->Remove(aContent);
   1.211 +  }
   1.212 +
   1.213 +  return NS_OK;
   1.214 +}
   1.215 +
   1.216 +void
   1.217 +nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
   1.218 +                                              nsIDocument* aOldDocument)
   1.219 +{
   1.220 +  NS_PRECONDITION(aOldDocument != nullptr, "no old document");
   1.221 +
   1.222 +  if (mDestroyed)
   1.223 +    return;
   1.224 +
   1.225 +  nsRefPtr<nsXBLBinding> binding = aContent->GetXBLBinding();
   1.226 +  if (binding) {
   1.227 +    binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement());
   1.228 +    binding->ChangeDocument(aOldDocument, nullptr);
   1.229 +    aContent->SetXBLBinding(nullptr, this);
   1.230 +  }
   1.231 +
   1.232 +  // Clear out insertion parents and content lists.
   1.233 +  aContent->SetXBLInsertionParent(nullptr);
   1.234 +}
   1.235 +
   1.236 +nsIAtom*
   1.237 +nsBindingManager::ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID)
   1.238 +{
   1.239 +  nsXBLBinding *binding = aContent->GetXBLBinding();
   1.240 +
   1.241 +  if (binding) {
   1.242 +    nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
   1.243 +
   1.244 +    if (base) {
   1.245 +      return base;
   1.246 +    }
   1.247 +  }
   1.248 +
   1.249 +  *aNameSpaceID = aContent->GetNameSpaceID();
   1.250 +  return aContent->Tag();
   1.251 +}
   1.252 +
   1.253 +nsresult
   1.254 +nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
   1.255 +                                       nsIDOMNodeList** aResult)
   1.256 +{
   1.257 +  NS_IF_ADDREF(*aResult = GetAnonymousNodesFor(aContent));
   1.258 +  return NS_OK;
   1.259 +}
   1.260 +
   1.261 +nsINodeList*
   1.262 +nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent)
   1.263 +{
   1.264 +  nsXBLBinding* binding = GetBindingWithContent(aContent);
   1.265 +  return binding ? binding->GetAnonymousNodeList() : nullptr;
   1.266 +}
   1.267 +
   1.268 +nsresult
   1.269 +nsBindingManager::ClearBinding(nsIContent* aContent)
   1.270 +{
   1.271 +  // Hold a ref to the binding so it won't die when we remove it from our table
   1.272 +  nsRefPtr<nsXBLBinding> binding =
   1.273 +    aContent ? aContent->GetXBLBinding() : nullptr;
   1.274 +
   1.275 +  if (!binding) {
   1.276 +    return NS_OK;
   1.277 +  }
   1.278 +
   1.279 +  // For now we can only handle removing a binding if it's the only one
   1.280 +  NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE);
   1.281 +
   1.282 +  // Hold strong ref in case removing the binding tries to close the
   1.283 +  // window or something.
   1.284 +  // XXXbz should that be ownerdoc?  Wouldn't we need a ref to the
   1.285 +  // currentdoc too?  What's the one that should be passed to
   1.286 +  // ChangeDocument?
   1.287 +  nsCOMPtr<nsIDocument> doc = aContent->OwnerDoc();
   1.288 +
   1.289 +  // Finally remove the binding...
   1.290 +  // XXXbz this doesn't remove the implementation!  Should fix!  Until
   1.291 +  // then we need the explicit UnhookEventHandlers here.
   1.292 +  binding->UnhookEventHandlers();
   1.293 +  binding->ChangeDocument(doc, nullptr);
   1.294 +  aContent->SetXBLBinding(nullptr, this);
   1.295 +  binding->MarkForDeath();
   1.296 +
   1.297 +  // ...and recreate its frames. We need to do this since the frames may have
   1.298 +  // been removed and style may have changed due to the removal of the
   1.299 +  // anonymous children.
   1.300 +  // XXXbz this should be using the current doc (if any), not the owner doc.
   1.301 +  nsIPresShell *presShell = doc->GetShell();
   1.302 +  NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
   1.303 +
   1.304 +  return presShell->RecreateFramesFor(aContent);;
   1.305 +}
   1.306 +
   1.307 +nsresult
   1.308 +nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
   1.309 +                                      nsIURI* aURL,
   1.310 +                                      nsIPrincipal* aOriginPrincipal)
   1.311 +{
   1.312 +  NS_PRECONDITION(aURL, "Must have a URI to load!");
   1.313 +
   1.314 +  // First we need to load our binding.
   1.315 +  nsXBLService* xblService = nsXBLService::GetInstance();
   1.316 +  if (!xblService)
   1.317 +    return NS_ERROR_FAILURE;
   1.318 +
   1.319 +  // Load the binding doc.
   1.320 +  nsRefPtr<nsXBLDocumentInfo> info;
   1.321 +  xblService->LoadBindingDocumentInfo(nullptr, aBoundDoc, aURL,
   1.322 +                                      aOriginPrincipal, true,
   1.323 +                                      getter_AddRefs(info));
   1.324 +  if (!info)
   1.325 +    return NS_ERROR_FAILURE;
   1.326 +
   1.327 +  return NS_OK;
   1.328 +}
   1.329 +
   1.330 +void
   1.331 +nsBindingManager::RemoveFromAttachedQueue(nsXBLBinding* aBinding)
   1.332 +{
   1.333 +  // Don't remove items here as that could mess up an executing
   1.334 +  // ProcessAttachedQueue. Instead, null the entry in the queue.
   1.335 +  uint32_t index = mAttachedStack.IndexOf(aBinding);
   1.336 +  if (index != mAttachedStack.NoIndex) {
   1.337 +    mAttachedStack[index] = nullptr;
   1.338 +  }
   1.339 +}
   1.340 +
   1.341 +nsresult
   1.342 +nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
   1.343 +{
   1.344 +  if (!mAttachedStack.AppendElement(aBinding))
   1.345 +    return NS_ERROR_OUT_OF_MEMORY;
   1.346 +
   1.347 +  // If we're in the middle of processing our queue already, don't
   1.348 +  // bother posting the event.
   1.349 +  if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) {
   1.350 +    PostProcessAttachedQueueEvent();
   1.351 +  }
   1.352 +
   1.353 +  // Make sure that flushes will flush out the new items as needed.
   1.354 +  mDocument->SetNeedStyleFlush();
   1.355 +
   1.356 +  return NS_OK;
   1.357 +
   1.358 +}
   1.359 +
   1.360 +void
   1.361 +nsBindingManager::PostProcessAttachedQueueEvent()
   1.362 +{
   1.363 +  mProcessAttachedQueueEvent =
   1.364 +    NS_NewRunnableMethod(this, &nsBindingManager::DoProcessAttachedQueue);
   1.365 +  nsresult rv = NS_DispatchToCurrentThread(mProcessAttachedQueueEvent);
   1.366 +  if (NS_SUCCEEDED(rv) && mDocument) {
   1.367 +    mDocument->BlockOnload();
   1.368 +  }
   1.369 +}
   1.370 +
   1.371 +void
   1.372 +nsBindingManager::DoProcessAttachedQueue()
   1.373 +{
   1.374 +  if (!mProcessingAttachedStack) {
   1.375 +    ProcessAttachedQueue();
   1.376 +
   1.377 +    NS_ASSERTION(mAttachedStack.Length() == 0,
   1.378 +               "Shouldn't have pending bindings!");
   1.379 +
   1.380 +    mProcessAttachedQueueEvent = nullptr;
   1.381 +  } else {
   1.382 +    // Someone's doing event processing from inside a constructor.
   1.383 +    // They're evil, but we'll fight back!  Just poll on them being
   1.384 +    // done and repost the attached queue event.
   1.385 +    PostProcessAttachedQueueEvent();
   1.386 +  }
   1.387 +
   1.388 +  // No matter what, unblock onload for the event that's fired.
   1.389 +  if (mDocument) {
   1.390 +    // Hold a strong reference while calling UnblockOnload since that might
   1.391 +    // run script.
   1.392 +    nsCOMPtr<nsIDocument> doc = mDocument;
   1.393 +    doc->UnblockOnload(true);
   1.394 +  }
   1.395 +}
   1.396 +
   1.397 +void
   1.398 +nsBindingManager::ProcessAttachedQueue(uint32_t aSkipSize)
   1.399 +{
   1.400 +  if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
   1.401 +    return;
   1.402 +
   1.403 +  mProcessingAttachedStack = true;
   1.404 +
   1.405 +  // Excute constructors. Do this from high index to low
   1.406 +  while (mAttachedStack.Length() > aSkipSize) {
   1.407 +    uint32_t lastItem = mAttachedStack.Length() - 1;
   1.408 +    nsRefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem);
   1.409 +    mAttachedStack.RemoveElementAt(lastItem);
   1.410 +    if (binding) {
   1.411 +      binding->ExecuteAttachedHandler();
   1.412 +    }
   1.413 +  }
   1.414 +
   1.415 +  // If NodeWillBeDestroyed has run we don't want to clobber
   1.416 +  // mProcessingAttachedStack set there.
   1.417 +  if (mDocument) {
   1.418 +    mProcessingAttachedStack = false;
   1.419 +  }
   1.420 +
   1.421 +  NS_ASSERTION(mAttachedStack.Length() == aSkipSize, "How did we get here?");
   1.422 +
   1.423 +  mAttachedStack.Compact();
   1.424 +}
   1.425 +
   1.426 +// Keep bindings and bound elements alive while executing detached handlers.
   1.427 +struct BindingTableReadClosure
   1.428 +{
   1.429 +  nsCOMArray<nsIContent> mBoundElements;
   1.430 +  nsBindingList          mBindings;
   1.431 +};
   1.432 +
   1.433 +static PLDHashOperator
   1.434 +AccumulateBindingsToDetach(nsRefPtrHashKey<nsIContent> *aKey,
   1.435 +                           void* aClosure)
   1.436 +{
   1.437 +  nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
   1.438 +  BindingTableReadClosure* closure =
   1.439 +    static_cast<BindingTableReadClosure*>(aClosure);
   1.440 +  if (binding && closure->mBindings.AppendElement(binding)) {
   1.441 +    if (!closure->mBoundElements.AppendObject(binding->GetBoundElement())) {
   1.442 +      closure->mBindings.RemoveElementAt(closure->mBindings.Length() - 1);
   1.443 +    }
   1.444 +  }
   1.445 +  return PL_DHASH_NEXT;
   1.446 +}
   1.447 +
   1.448 +void
   1.449 +nsBindingManager::ExecuteDetachedHandlers()
   1.450 +{
   1.451 +  // Walk our hashtable of bindings.
   1.452 +  if (mBoundContentSet) {
   1.453 +    BindingTableReadClosure closure;
   1.454 +    mBoundContentSet->EnumerateEntries(AccumulateBindingsToDetach, &closure);
   1.455 +    uint32_t i, count = closure.mBindings.Length();
   1.456 +    for (i = 0; i < count; ++i) {
   1.457 +      closure.mBindings[i]->ExecuteDetachedHandler();
   1.458 +    }
   1.459 +  }
   1.460 +}
   1.461 +
   1.462 +nsresult
   1.463 +nsBindingManager::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
   1.464 +{
   1.465 +  NS_PRECONDITION(aDocumentInfo, "Must have a non-null documentinfo!");
   1.466 +
   1.467 +  if (!mDocumentTable) {
   1.468 +    mDocumentTable = new nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>(16);
   1.469 +  }
   1.470 +
   1.471 +  mDocumentTable->Put(aDocumentInfo->DocumentURI(), aDocumentInfo);
   1.472 +
   1.473 +  return NS_OK;
   1.474 +}
   1.475 +
   1.476 +void
   1.477 +nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
   1.478 +{
   1.479 +  if (mDocumentTable) {
   1.480 +    mDocumentTable->Remove(aDocumentInfo->DocumentURI());
   1.481 +  }
   1.482 +}
   1.483 +
   1.484 +nsXBLDocumentInfo*
   1.485 +nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL)
   1.486 +{
   1.487 +  if (!mDocumentTable)
   1.488 +    return nullptr;
   1.489 +
   1.490 +  return mDocumentTable->GetWeak(aURL);
   1.491 +}
   1.492 +
   1.493 +nsresult
   1.494 +nsBindingManager::PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener)
   1.495 +{
   1.496 +  NS_PRECONDITION(aListener, "Must have a non-null listener!");
   1.497 +
   1.498 +  if (!mLoadingDocTable) {
   1.499 +    mLoadingDocTable = new nsInterfaceHashtable<nsURIHashKey,nsIStreamListener>(16);
   1.500 +  }
   1.501 +  mLoadingDocTable->Put(aURL, aListener);
   1.502 +
   1.503 +  return NS_OK;
   1.504 +}
   1.505 +
   1.506 +nsIStreamListener*
   1.507 +nsBindingManager::GetLoadingDocListener(nsIURI* aURL)
   1.508 +{
   1.509 +  if (!mLoadingDocTable)
   1.510 +    return nullptr;
   1.511 +
   1.512 +  return mLoadingDocTable->GetWeak(aURL);
   1.513 +}
   1.514 +
   1.515 +void
   1.516 +nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL)
   1.517 +{
   1.518 +  if (mLoadingDocTable) {
   1.519 +    mLoadingDocTable->Remove(aURL);
   1.520 +  }
   1.521 +}
   1.522 +
   1.523 +static PLDHashOperator
   1.524 +MarkForDeath(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
   1.525 +{
   1.526 +  nsXBLBinding *binding = aKey->GetKey()->GetXBLBinding();
   1.527 +
   1.528 +  if (binding->MarkedForDeath())
   1.529 +    return PL_DHASH_NEXT; // Already marked for death.
   1.530 +
   1.531 +  nsAutoCString path;
   1.532 +  binding->PrototypeBinding()->DocURI()->GetPath(path);
   1.533 +
   1.534 +  if (!strncmp(path.get(), "/skin", 5))
   1.535 +    binding->MarkForDeath();
   1.536 +
   1.537 +  return PL_DHASH_NEXT;
   1.538 +}
   1.539 +
   1.540 +void
   1.541 +nsBindingManager::FlushSkinBindings()
   1.542 +{
   1.543 +  if (mBoundContentSet) {
   1.544 +    mBoundContentSet->EnumerateEntries(MarkForDeath, nullptr);
   1.545 +  }
   1.546 +}
   1.547 +
   1.548 +// Used below to protect from recurring in QI calls through XPConnect.
   1.549 +struct AntiRecursionData {
   1.550 +  nsIContent* element;
   1.551 +  REFNSIID iid;
   1.552 +  AntiRecursionData* next;
   1.553 +
   1.554 +  AntiRecursionData(nsIContent* aElement,
   1.555 +                    REFNSIID aIID,
   1.556 +                    AntiRecursionData* aNext)
   1.557 +    : element(aElement), iid(aIID), next(aNext) {}
   1.558 +};
   1.559 +
   1.560 +nsresult
   1.561 +nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
   1.562 +                                           void** aResult)
   1.563 +{
   1.564 +  *aResult = nullptr;
   1.565 +  nsXBLBinding *binding = aContent ? aContent->GetXBLBinding() : nullptr;
   1.566 +  if (binding) {
   1.567 +    // The binding should not be asked for nsISupports
   1.568 +    NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports");
   1.569 +    if (binding->ImplementsInterface(aIID)) {
   1.570 +      nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent);
   1.571 +
   1.572 +      if (wrappedJS) {
   1.573 +        // Protect from recurring in QI calls through XPConnect.
   1.574 +        // This can happen when a second binding is being resolved.
   1.575 +        // At that point a wrappedJS exists, but it doesn't yet know about
   1.576 +        // the iid we are asking for. So, without this protection,
   1.577 +        // AggregatedQueryInterface would end up recurring back into itself
   1.578 +        // through this code.
   1.579 +        //
   1.580 +        // With this protection, when we detect the recursion we return
   1.581 +        // NS_NOINTERFACE in the inner call. The outer call will then fall
   1.582 +        // through (see below) and build a new chained wrappedJS for the iid.
   1.583 +        //
   1.584 +        // We're careful to not assume that only one direct nesting can occur
   1.585 +        // because there is a call into JS in the middle and we can't assume
   1.586 +        // that this code won't be reached by some more complex nesting path.
   1.587 +        //
   1.588 +        // NOTE: We *assume* this is single threaded, so we can use a
   1.589 +        // static linked list to do the check.
   1.590 +
   1.591 +        static AntiRecursionData* list = nullptr;
   1.592 +
   1.593 +        for (AntiRecursionData* p = list; p; p = p->next) {
   1.594 +          if (p->element == aContent && p->iid.Equals(aIID)) {
   1.595 +            *aResult = nullptr;
   1.596 +            return NS_NOINTERFACE;
   1.597 +          }
   1.598 +        }
   1.599 +
   1.600 +        AntiRecursionData item(aContent, aIID, list);
   1.601 +        list = &item;
   1.602 +
   1.603 +        nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
   1.604 +
   1.605 +        list = item.next;
   1.606 +
   1.607 +        if (*aResult)
   1.608 +          return rv;
   1.609 +
   1.610 +        // No result was found, so this must be another XBL interface.
   1.611 +        // Fall through to create a new wrapper.
   1.612 +      }
   1.613 +
   1.614 +      // We have never made a wrapper for this implementation.
   1.615 +      // Create an XPC wrapper for the script object and hand it back.
   1.616 +
   1.617 +      nsIDocument* doc = aContent->OwnerDoc();
   1.618 +
   1.619 +      nsCOMPtr<nsIScriptGlobalObject> global =
   1.620 +        do_QueryInterface(doc->GetWindow());
   1.621 +      if (!global)
   1.622 +        return NS_NOINTERFACE;
   1.623 +
   1.624 +      nsIScriptContext *context = global->GetContext();
   1.625 +      if (!context)
   1.626 +        return NS_NOINTERFACE;
   1.627 +
   1.628 +      AutoPushJSContext cx(context->GetNativeContext());
   1.629 +      if (!cx)
   1.630 +        return NS_NOINTERFACE;
   1.631 +
   1.632 +      nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
   1.633 +
   1.634 +      JS::Rooted<JSObject*> jsobj(cx, aContent->GetWrapper());
   1.635 +      NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE);
   1.636 +
   1.637 +      // If we're using an XBL scope, we need to use the Xray view to the bound
   1.638 +      // content in order to view the full array of methods defined in the
   1.639 +      // binding, some of which may not be exposed on the prototype of
   1.640 +      // untrusted content.
   1.641 +      //
   1.642 +      // If there's no separate XBL scope, or if the reflector itself lives in
   1.643 +      // the XBL scope, we'll end up with the global of the reflector, and this
   1.644 +      // will all be a no-op.
   1.645 +      JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, jsobj));
   1.646 +      NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED);
   1.647 +      JSAutoCompartment ac(cx, xblScope);
   1.648 +      bool ok = JS_WrapObject(cx, &jsobj);
   1.649 +      NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   1.650 +      MOZ_ASSERT_IF(js::IsWrapper(jsobj), xpc::IsXrayWrapper(jsobj));
   1.651 +
   1.652 +      nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, cx,
   1.653 +                                                        jsobj, aIID, aResult);
   1.654 +      if (NS_FAILED(rv))
   1.655 +        return rv;
   1.656 +
   1.657 +      // We successfully created a wrapper.  We will own this wrapper for as long as the binding remains
   1.658 +      // alive.  At the time the binding is cleared out of the bindingManager, we will remove the wrapper
   1.659 +      // from the bindingManager as well.
   1.660 +      nsISupports* supp = static_cast<nsISupports*>(*aResult);
   1.661 +      wrappedJS = do_QueryInterface(supp);
   1.662 +      SetWrappedJS(aContent, wrappedJS);
   1.663 +
   1.664 +      return rv;
   1.665 +    }
   1.666 +  }
   1.667 +
   1.668 +  *aResult = nullptr;
   1.669 +  return NS_NOINTERFACE;
   1.670 +}
   1.671 +
   1.672 +nsresult
   1.673 +nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
   1.674 +                            ElementDependentRuleProcessorData* aData,
   1.675 +                            bool* aCutOffInheritance)
   1.676 +{
   1.677 +  *aCutOffInheritance = false;
   1.678 +
   1.679 +  NS_ASSERTION(aData->mElement, "How did that happen?");
   1.680 +
   1.681 +  // Walk the binding scope chain, starting with the binding attached to our
   1.682 +  // content, up till we run out of scopes or we get cut off.
   1.683 +  nsIContent *content = aData->mElement;
   1.684 +
   1.685 +  do {
   1.686 +    nsXBLBinding *binding = content->GetXBLBinding();
   1.687 +    if (binding) {
   1.688 +      aData->mTreeMatchContext.mScopedRoot = content;
   1.689 +      binding->WalkRules(aFunc, aData);
   1.690 +      // If we're not looking at our original content, allow the binding to cut
   1.691 +      // off style inheritance
   1.692 +      if (content != aData->mElement) {
   1.693 +        if (!binding->InheritsStyle()) {
   1.694 +          // Go no further; we're not inheriting style from anything above here
   1.695 +          break;
   1.696 +        }
   1.697 +      }
   1.698 +    }
   1.699 +
   1.700 +    if (content->IsRootOfNativeAnonymousSubtree()) {
   1.701 +      break; // Deliberately cut off style inheritance here.
   1.702 +    }
   1.703 +
   1.704 +    content = content->GetBindingParent();
   1.705 +  } while (content);
   1.706 +
   1.707 +  // If "content" is non-null that means we cut off inheritance at some point
   1.708 +  // in the loop.
   1.709 +  *aCutOffInheritance = (content != nullptr);
   1.710 +
   1.711 +  // Null out the scoped root that we set repeatedly
   1.712 +  aData->mTreeMatchContext.mScopedRoot = nullptr;
   1.713 +
   1.714 +  return NS_OK;
   1.715 +}
   1.716 +
   1.717 +typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
   1.718 +
   1.719 +static PLDHashOperator
   1.720 +EnumRuleProcessors(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
   1.721 +{
   1.722 +  nsIContent *boundContent = aKey->GetKey();
   1.723 +  nsAutoPtr<RuleProcessorSet> *set = static_cast<nsAutoPtr<RuleProcessorSet>*>(aClosure);
   1.724 +  for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
   1.725 +       binding = binding->GetBaseBinding()) {
   1.726 +    nsIStyleRuleProcessor *ruleProc =
   1.727 +      binding->PrototypeBinding()->GetRuleProcessor();
   1.728 +    if (ruleProc) {
   1.729 +      if (!(*set)) {
   1.730 +        *set = new RuleProcessorSet;
   1.731 +      }
   1.732 +      (*set)->PutEntry(ruleProc);
   1.733 +    }
   1.734 +  }
   1.735 +  return PL_DHASH_NEXT;
   1.736 +}
   1.737 +
   1.738 +struct WalkAllRulesData {
   1.739 +  nsIStyleRuleProcessor::EnumFunc mFunc;
   1.740 +  ElementDependentRuleProcessorData* mData;
   1.741 +};
   1.742 +
   1.743 +static PLDHashOperator
   1.744 +EnumWalkAllRules(nsPtrHashKey<nsIStyleRuleProcessor> *aKey, void* aClosure)
   1.745 +{
   1.746 +  nsIStyleRuleProcessor *ruleProcessor = aKey->GetKey();
   1.747 +
   1.748 +  WalkAllRulesData *data = static_cast<WalkAllRulesData*>(aClosure);
   1.749 +
   1.750 +  (*(data->mFunc))(ruleProcessor, data->mData);
   1.751 +
   1.752 +  return PL_DHASH_NEXT;
   1.753 +}
   1.754 +
   1.755 +void
   1.756 +nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
   1.757 +                               ElementDependentRuleProcessorData* aData)
   1.758 +{
   1.759 +  if (!mBoundContentSet) {
   1.760 +    return;
   1.761 +  }
   1.762 +
   1.763 +  nsAutoPtr<RuleProcessorSet> set;
   1.764 +  mBoundContentSet->EnumerateEntries(EnumRuleProcessors, &set);
   1.765 +  if (!set)
   1.766 +    return;
   1.767 +
   1.768 +  WalkAllRulesData data = { aFunc, aData };
   1.769 +  set->EnumerateEntries(EnumWalkAllRules, &data);
   1.770 +}
   1.771 +
   1.772 +struct MediumFeaturesChangedData {
   1.773 +  nsPresContext *mPresContext;
   1.774 +  bool *mRulesChanged;
   1.775 +};
   1.776 +
   1.777 +static PLDHashOperator
   1.778 +EnumMediumFeaturesChanged(nsPtrHashKey<nsIStyleRuleProcessor> *aKey, void* aClosure)
   1.779 +{
   1.780 +  nsIStyleRuleProcessor *ruleProcessor = aKey->GetKey();
   1.781 +
   1.782 +  MediumFeaturesChangedData *data =
   1.783 +    static_cast<MediumFeaturesChangedData*>(aClosure);
   1.784 +
   1.785 +  bool thisChanged = ruleProcessor->MediumFeaturesChanged(data->mPresContext);
   1.786 +  *data->mRulesChanged = *data->mRulesChanged || thisChanged;
   1.787 +
   1.788 +  return PL_DHASH_NEXT;
   1.789 +}
   1.790 +
   1.791 +nsresult
   1.792 +nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
   1.793 +                                        bool* aRulesChanged)
   1.794 +{
   1.795 +  *aRulesChanged = false;
   1.796 +  if (!mBoundContentSet) {
   1.797 +    return NS_OK;
   1.798 +  }
   1.799 +
   1.800 +  nsAutoPtr<RuleProcessorSet> set;
   1.801 +  mBoundContentSet->EnumerateEntries(EnumRuleProcessors, &set);
   1.802 +  if (!set) {
   1.803 +    return NS_OK;
   1.804 +  }
   1.805 +
   1.806 +  MediumFeaturesChangedData data = { aPresContext, aRulesChanged };
   1.807 +  set->EnumerateEntries(EnumMediumFeaturesChanged, &data);
   1.808 +  return NS_OK;
   1.809 +}
   1.810 +
   1.811 +static PLDHashOperator
   1.812 +EnumAppendAllSheets(nsRefPtrHashKey<nsIContent> *aKey, void* aClosure)
   1.813 +{
   1.814 +  nsIContent *boundContent = aKey->GetKey();
   1.815 +  nsTArray<nsCSSStyleSheet*>* array =
   1.816 +    static_cast<nsTArray<nsCSSStyleSheet*>*>(aClosure);
   1.817 +  for (nsXBLBinding *binding = boundContent->GetXBLBinding(); binding;
   1.818 +       binding = binding->GetBaseBinding()) {
   1.819 +    nsXBLPrototypeResources::sheet_array_type* sheets =
   1.820 +      binding->PrototypeBinding()->GetStyleSheets();
   1.821 +    if (sheets) {
   1.822 +      // Copy from nsTArray<nsRefPtr<nsCSSStyleSheet> > to
   1.823 +      // nsTArray<nsCSSStyleSheet*>.
   1.824 +      array->AppendElements(*sheets);
   1.825 +    }
   1.826 +  }
   1.827 +  return PL_DHASH_NEXT;
   1.828 +}
   1.829 +
   1.830 +void
   1.831 +nsBindingManager::AppendAllSheets(nsTArray<nsCSSStyleSheet*>& aArray)
   1.832 +{
   1.833 +  if (mBoundContentSet) {
   1.834 +    mBoundContentSet->EnumerateEntries(EnumAppendAllSheets, &aArray);
   1.835 +  }
   1.836 +}
   1.837 +
   1.838 +static void
   1.839 +InsertAppendedContent(XBLChildrenElement* aPoint,
   1.840 +                      nsIContent* aFirstNewContent)
   1.841 +{
   1.842 +  uint32_t insertionIndex;
   1.843 +  if (nsIContent* prevSibling = aFirstNewContent->GetPreviousSibling()) {
   1.844 +    // If we have a previous sibling, then it must already be in aPoint. Find
   1.845 +    // it and insert after it.
   1.846 +    insertionIndex = aPoint->IndexOfInsertedChild(prevSibling);
   1.847 +    MOZ_ASSERT(insertionIndex != aPoint->NoIndex);
   1.848 +
   1.849 +    // Our insertion index is one after our previous sibling's index.
   1.850 +    ++insertionIndex;
   1.851 +  } else {
   1.852 +    // Otherwise, we append.
   1.853 +    // TODO This is wrong for nested insertion points. In that case, we need to
   1.854 +    // keep track of the right index to insert into.
   1.855 +    insertionIndex = aPoint->mInsertedChildren.Length();
   1.856 +  }
   1.857 +
   1.858 +  // Do the inserting.
   1.859 +  for (nsIContent* currentChild = aFirstNewContent;
   1.860 +       currentChild;
   1.861 +       currentChild = currentChild->GetNextSibling()) {
   1.862 +    aPoint->InsertInsertedChildAt(currentChild, insertionIndex++);
   1.863 +  }
   1.864 +}
   1.865 +
   1.866 +void
   1.867 +nsBindingManager::ContentAppended(nsIDocument* aDocument,
   1.868 +                                  nsIContent* aContainer,
   1.869 +                                  nsIContent* aFirstNewContent,
   1.870 +                                  int32_t     aNewIndexInContainer)
   1.871 +{
   1.872 +  if (aNewIndexInContainer == -1) {
   1.873 +    return;
   1.874 +  }
   1.875 +
   1.876 +  // Try to find insertion points for all the new kids.
   1.877 +  XBLChildrenElement* point = nullptr;
   1.878 +  nsIContent* parent = aContainer;
   1.879 +  bool first = true;
   1.880 +  do {
   1.881 +    nsXBLBinding* binding = GetBindingWithContent(parent);
   1.882 +    if (!binding) {
   1.883 +      break;
   1.884 +    }
   1.885 +
   1.886 +    if (binding->HasFilteredInsertionPoints()) {
   1.887 +      // There are filtered insertion points involved, handle each child
   1.888 +      // separately.
   1.889 +      // We could optimize this in the case when we've nested a few levels
   1.890 +      // deep already, without hitting bindings that have filtered insertion
   1.891 +      // points.
   1.892 +      int32_t currentIndex = aNewIndexInContainer;
   1.893 +      for (nsIContent* currentChild = aFirstNewContent; currentChild;
   1.894 +           currentChild = currentChild->GetNextSibling()) {
   1.895 +        HandleChildInsertion(aContainer, currentChild,
   1.896 +                             currentIndex++, true);
   1.897 +      }
   1.898 +
   1.899 +      return;
   1.900 +    }
   1.901 +
   1.902 +    point = binding->GetDefaultInsertionPoint();
   1.903 +    if (!point) {
   1.904 +      break;
   1.905 +    }
   1.906 +
   1.907 +    // Even though we're in ContentAppended, nested insertion points force us
   1.908 +    // to deal with this append as an insertion except in the outermost
   1.909 +    // binding.
   1.910 +    if (first) {
   1.911 +      first = false;
   1.912 +      for (nsIContent* child = aFirstNewContent; child;
   1.913 +           child = child->GetNextSibling()) {
   1.914 +        point->AppendInsertedChild(child);
   1.915 +      }
   1.916 +    } else {
   1.917 +      InsertAppendedContent(point, aFirstNewContent);
   1.918 +    }
   1.919 +
   1.920 +    nsIContent* newParent = point->GetParent();
   1.921 +    if (newParent == parent) {
   1.922 +      break;
   1.923 +    }
   1.924 +    parent = newParent;
   1.925 +  } while (parent);
   1.926 +}
   1.927 +
   1.928 +void
   1.929 +nsBindingManager::ContentInserted(nsIDocument* aDocument,
   1.930 +                                  nsIContent* aContainer,
   1.931 +                                  nsIContent* aChild,
   1.932 +                                  int32_t aIndexInContainer)
   1.933 +{
   1.934 +  if (aIndexInContainer == -1) {
   1.935 +    return;
   1.936 +  }
   1.937 +
   1.938 +  HandleChildInsertion(aContainer, aChild, aIndexInContainer, false);
   1.939 +}
   1.940 +
   1.941 +void
   1.942 +nsBindingManager::ContentRemoved(nsIDocument* aDocument,
   1.943 +                                 nsIContent* aContainer,
   1.944 +                                 nsIContent* aChild,
   1.945 +                                 int32_t aIndexInContainer,
   1.946 +                                 nsIContent* aPreviousSibling)
   1.947 +{
   1.948 +  aChild->SetXBLInsertionParent(nullptr);
   1.949 +
   1.950 +  XBLChildrenElement* point = nullptr;
   1.951 +  nsIContent* parent = aContainer;
   1.952 +  do {
   1.953 +    nsXBLBinding* binding = GetBindingWithContent(parent);
   1.954 +    if (!binding) {
   1.955 +      // If aChild is XBL content, it might have <xbl:children> elements
   1.956 +      // somewhere under it. We need to inform those elements that they're no
   1.957 +      // longer in the tree so they can tell their distributed children that
   1.958 +      // they're no longer distributed under them.
   1.959 +      // XXX This is wrong. We need to do far more work to update the parent
   1.960 +      // binding's list of insertion points and to get the new insertion parent
   1.961 +      // for the newly-distributed children correct.
   1.962 +      if (aChild->GetBindingParent()) {
   1.963 +        ClearInsertionPointsRecursively(aChild);
   1.964 +      }
   1.965 +      return;
   1.966 +    }
   1.967 +
   1.968 +    point = binding->FindInsertionPointFor(aChild);
   1.969 +    if (!point) {
   1.970 +      break;
   1.971 +    }
   1.972 +
   1.973 +    point->RemoveInsertedChild(aChild);
   1.974 +
   1.975 +    nsIContent* newParent = point->GetParent();
   1.976 +    if (newParent == parent) {
   1.977 +      break;
   1.978 +    }
   1.979 +    parent = newParent;
   1.980 +  } while (parent);
   1.981 +}
   1.982 +
   1.983 +void
   1.984 +nsBindingManager::ClearInsertionPointsRecursively(nsIContent* aContent)
   1.985 +{
   1.986 +  if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
   1.987 +    static_cast<XBLChildrenElement*>(aContent)->ClearInsertedChildren();
   1.988 +  }
   1.989 +
   1.990 +  for (nsIContent* child = aContent->GetFirstChild(); child;
   1.991 +       child = child->GetNextSibling()) {
   1.992 +    ClearInsertionPointsRecursively(child);
   1.993 +  }
   1.994 +}
   1.995 +
   1.996 +void
   1.997 +nsBindingManager::DropDocumentReference()
   1.998 +{
   1.999 +  mDestroyed = true;
  1.1000 +
  1.1001 +  // Make sure to not run any more XBL constructors
  1.1002 +  mProcessingAttachedStack = true;
  1.1003 +  if (mProcessAttachedQueueEvent) {
  1.1004 +    mProcessAttachedQueueEvent->Revoke();
  1.1005 +  }
  1.1006 +
  1.1007 +  if (mBoundContentSet) {
  1.1008 +    mBoundContentSet->Clear();
  1.1009 +  }
  1.1010 +
  1.1011 +  mDocument = nullptr;
  1.1012 +}
  1.1013 +
  1.1014 +void
  1.1015 +nsBindingManager::Traverse(nsIContent *aContent,
  1.1016 +                           nsCycleCollectionTraversalCallback &cb)
  1.1017 +{
  1.1018 +  if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
  1.1019 +      !aContent->IsElement()) {
  1.1020 +    // Don't traverse if content is not in this binding manager.
  1.1021 +    // We also don't traverse non-elements because there should not
  1.1022 +    // be bindings (checking the flag alone is not sufficient because
  1.1023 +    // the flag is also set on children of insertion points that may be
  1.1024 +    // non-elements).
  1.1025 +    return;
  1.1026 +  }
  1.1027 +
  1.1028 +  if (mBoundContentSet && mBoundContentSet->Contains(aContent)) {
  1.1029 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBoundContentSet entry");
  1.1030 +    cb.NoteXPCOMChild(aContent);
  1.1031 +  }
  1.1032 +
  1.1033 +  nsIXPConnectWrappedJS *value = GetWrappedJS(aContent);
  1.1034 +  if (value) {
  1.1035 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
  1.1036 +    cb.NoteXPCOMChild(aContent);
  1.1037 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
  1.1038 +    cb.NoteXPCOMChild(value);
  1.1039 +  }
  1.1040 +}
  1.1041 +
  1.1042 +void
  1.1043 +nsBindingManager::BeginOutermostUpdate()
  1.1044 +{
  1.1045 +  mAttachedStackSizeOnOutermost = mAttachedStack.Length();
  1.1046 +}
  1.1047 +
  1.1048 +void
  1.1049 +nsBindingManager::EndOutermostUpdate()
  1.1050 +{
  1.1051 +  if (!mProcessingAttachedStack) {
  1.1052 +    ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
  1.1053 +    mAttachedStackSizeOnOutermost = 0;
  1.1054 +  }
  1.1055 +}
  1.1056 +
  1.1057 +void
  1.1058 +nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
  1.1059 +                                       nsIContent* aChild,
  1.1060 +                                       uint32_t aIndexInContainer,
  1.1061 +                                       bool aAppend)
  1.1062 +{
  1.1063 +  NS_PRECONDITION(aChild, "Must have child");
  1.1064 +  NS_PRECONDITION(!aContainer ||
  1.1065 +                  uint32_t(aContainer->IndexOf(aChild)) == aIndexInContainer,
  1.1066 +                  "Child not at the right index?");
  1.1067 +
  1.1068 +  XBLChildrenElement* point = nullptr;
  1.1069 +  nsIContent* parent = aContainer;
  1.1070 +  while (parent) {
  1.1071 +    nsXBLBinding* binding = GetBindingWithContent(parent);
  1.1072 +    if (!binding) {
  1.1073 +      break;
  1.1074 +    }
  1.1075 +
  1.1076 +    point = binding->FindInsertionPointFor(aChild);
  1.1077 +    if (!point) {
  1.1078 +      break;
  1.1079 +    }
  1.1080 +
  1.1081 +    // Insert the child into the proper insertion point.
  1.1082 +    // TODO If there were multiple insertion points, this approximation can be
  1.1083 +    // wrong. We need to re-run the distribution algorithm. In the meantime,
  1.1084 +    // this should work well enough.
  1.1085 +    uint32_t index = aAppend ? point->mInsertedChildren.Length() : 0;
  1.1086 +    for (nsIContent* currentSibling = aChild->GetPreviousSibling();
  1.1087 +         currentSibling;
  1.1088 +         currentSibling = currentSibling->GetPreviousSibling()) {
  1.1089 +      // If we find one of our previous siblings in the insertion point, the
  1.1090 +      // index following it is the correct insertion point. Otherwise, we guess
  1.1091 +      // based on whether we're appending or inserting.
  1.1092 +      uint32_t pointIndex = point->IndexOfInsertedChild(currentSibling);
  1.1093 +      if (pointIndex != point->NoIndex) {
  1.1094 +        index = pointIndex + 1;
  1.1095 +        break;
  1.1096 +      }
  1.1097 +    }
  1.1098 +
  1.1099 +    point->InsertInsertedChildAt(aChild, index);
  1.1100 +
  1.1101 +    nsIContent* newParent = point->GetParent();
  1.1102 +    if (newParent == parent) {
  1.1103 +      break;
  1.1104 +    }
  1.1105 +
  1.1106 +    parent = newParent;
  1.1107 +  }
  1.1108 +}
  1.1109 +
  1.1110 +
  1.1111 +nsIContent*
  1.1112 +nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
  1.1113 +                                           nsIContent* aChild)
  1.1114 +{
  1.1115 +  NS_PRECONDITION(aChild->GetParent() == aContainer,
  1.1116 +                  "Wrong container");
  1.1117 +
  1.1118 +  nsIContent* parent = aContainer;
  1.1119 +  if (aContainer->IsActiveChildrenElement()) {
  1.1120 +    if (static_cast<XBLChildrenElement*>(aContainer)->
  1.1121 +          HasInsertedChildren()) {
  1.1122 +      return nullptr;
  1.1123 +    }
  1.1124 +    parent = aContainer->GetParent();
  1.1125 +  }
  1.1126 +
  1.1127 +  while (parent) {
  1.1128 +    nsXBLBinding* binding = GetBindingWithContent(parent);
  1.1129 +    if (!binding) {
  1.1130 +      break;
  1.1131 +    }
  1.1132 +
  1.1133 +    XBLChildrenElement* point = binding->FindInsertionPointFor(aChild);
  1.1134 +    if (!point) {
  1.1135 +      return nullptr;
  1.1136 +    }
  1.1137 +
  1.1138 +    nsIContent* newParent = point->GetParent();
  1.1139 +    if (newParent == parent) {
  1.1140 +      break;
  1.1141 +    }
  1.1142 +    parent = newParent;
  1.1143 +  }
  1.1144 +
  1.1145 +  return parent;
  1.1146 +}
  1.1147 +
  1.1148 +nsIContent*
  1.1149 +nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
  1.1150 +                                                 bool* aMulti)
  1.1151 +{
  1.1152 +  *aMulti = false;
  1.1153 +
  1.1154 +  nsIContent* parent = aContainer;
  1.1155 +  if (aContainer->IsActiveChildrenElement()) {
  1.1156 +    if (static_cast<XBLChildrenElement*>(aContainer)->
  1.1157 +          HasInsertedChildren()) {
  1.1158 +      return nullptr;
  1.1159 +    }
  1.1160 +    parent = aContainer->GetParent();
  1.1161 +  }
  1.1162 +
  1.1163 +  while(parent) {
  1.1164 +    nsXBLBinding* binding = GetBindingWithContent(parent);
  1.1165 +    if (!binding) {
  1.1166 +      break;
  1.1167 +    }
  1.1168 +
  1.1169 +    if (binding->HasFilteredInsertionPoints()) {
  1.1170 +      *aMulti = true;
  1.1171 +      return nullptr;
  1.1172 +    }
  1.1173 +
  1.1174 +    XBLChildrenElement* point = binding->GetDefaultInsertionPoint();
  1.1175 +    if (!point) {
  1.1176 +      return nullptr;
  1.1177 +    }
  1.1178 +
  1.1179 +    nsIContent* newParent = point->GetParent();
  1.1180 +    if (newParent == parent) {
  1.1181 +      break;
  1.1182 +    }
  1.1183 +    parent = newParent;
  1.1184 +  }
  1.1185 +
  1.1186 +  return parent;
  1.1187 +}

mercurial