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.

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

mercurial