Coverage Report

Created: 2018-09-25 14:53

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