Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsContentList.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
/*
8
 * nsBaseContentList is a basic list of content nodes; nsContentList
9
 * is a commonly used NodeList implementation (used for
10
 * getElementsByTagName, some properties on HTMLDocument/Document, etc).
11
 */
12
13
#include "nsContentList.h"
14
#include "nsIContent.h"
15
#include "nsIDocument.h"
16
#include "mozilla/dom/Element.h"
17
#include "nsWrapperCacheInlines.h"
18
#include "nsContentUtils.h"
19
#include "nsCCUncollectableMarker.h"
20
#include "nsGkAtoms.h"
21
#include "mozilla/dom/HTMLCollectionBinding.h"
22
#include "mozilla/dom/NodeListBinding.h"
23
#include "mozilla/Likely.h"
24
#include "nsGenericHTMLElement.h"
25
#include "jsfriendapi.h"
26
#include <algorithm>
27
#include "mozilla/dom/NodeInfoInlines.h"
28
#include "mozilla/MruCache.h"
29
30
#include "PLDHashTable.h"
31
32
#ifdef DEBUG_CONTENT_LIST
33
#include "nsIContentIterator.h"
34
#define ASSERT_IN_SYNC AssertInSync()
35
#else
36
0
#define ASSERT_IN_SYNC PR_BEGIN_MACRO PR_END_MACRO
37
#endif
38
39
using namespace mozilla;
40
using namespace mozilla::dom;
41
42
nsBaseContentList::~nsBaseContentList()
43
0
{
44
0
}
45
46
NS_IMPL_CYCLE_COLLECTION_CLASS(nsBaseContentList)
47
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList)
48
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
49
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
50
0
  tmp->RemoveFromCaches();
51
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
52
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList)
53
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
54
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
55
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsBaseContentList)
56
57
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsBaseContentList)
58
0
  if (nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper()) {
59
0
    for (uint32_t i = 0; i < tmp->mElements.Length(); ++i) {
60
0
      nsIContent* c = tmp->mElements[i];
61
0
      if (c->IsPurple()) {
62
0
        c->RemovePurple();
63
0
      }
64
0
      Element::MarkNodeChildren(c);
65
0
    }
66
0
    return true;
67
0
  }
68
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
69
0
70
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsBaseContentList)
71
0
  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
72
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
73
74
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsBaseContentList)
75
0
  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
76
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
77
78
// QueryInterface implementation for nsBaseContentList
79
0
NS_INTERFACE_TABLE_HEAD(nsBaseContentList)
80
0
  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
81
0
  NS_INTERFACE_TABLE(nsBaseContentList, nsINodeList)
82
0
  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsBaseContentList)
83
0
NS_INTERFACE_MAP_END
84
85
86
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBaseContentList)
87
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsBaseContentList,
88
                                                   LastRelease())
89
90
nsIContent*
91
nsBaseContentList::Item(uint32_t aIndex)
92
0
{
93
0
  return mElements.SafeElementAt(aIndex);
94
0
}
95
96
97
int32_t
98
nsBaseContentList::IndexOf(nsIContent *aContent, bool aDoFlush)
99
0
{
100
0
  return mElements.IndexOf(aContent);
101
0
}
102
103
int32_t
104
nsBaseContentList::IndexOf(nsIContent* aContent)
105
0
{
106
0
  return IndexOf(aContent, true);
107
0
}
108
109
size_t
110
nsBaseContentList::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
111
0
{
112
0
  size_t n = aMallocSizeOf(this);
113
0
  n += mElements.ShallowSizeOfExcludingThis(aMallocSizeOf);
114
0
  return n;
115
0
}
116
117
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsSimpleContentList, nsBaseContentList,
118
                                   mRoot)
119
120
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSimpleContentList)
121
0
NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
122
123
124
NS_IMPL_ADDREF_INHERITED(nsSimpleContentList, nsBaseContentList)
125
NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList)
126
127
JSObject*
128
nsSimpleContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
129
0
{
130
0
  return NodeList_Binding::Wrap(cx, this, aGivenProto);
131
0
}
132
133
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsEmptyContentList, nsBaseContentList, mRoot)
134
135
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEmptyContentList)
136
0
  NS_INTERFACE_MAP_ENTRY(nsIHTMLCollection)
137
0
NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
138
139
140
NS_IMPL_ADDREF_INHERITED(nsEmptyContentList, nsBaseContentList)
141
NS_IMPL_RELEASE_INHERITED(nsEmptyContentList, nsBaseContentList)
142
143
JSObject*
144
nsEmptyContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
145
0
{
146
0
  return HTMLCollection_Binding::Wrap(cx, this, aGivenProto);
147
0
}
148
149
mozilla::dom::Element*
150
nsEmptyContentList::GetElementAt(uint32_t index)
151
0
{
152
0
  return nullptr;
153
0
}
154
155
mozilla::dom::Element*
156
nsEmptyContentList::GetFirstNamedElement(const nsAString& aName, bool& aFound)
157
0
{
158
0
  aFound = false;
159
0
  return nullptr;
160
0
}
161
162
void
163
nsEmptyContentList::GetSupportedNames(nsTArray<nsString>& aNames)
164
0
{
165
0
}
166
167
nsIContent*
168
nsEmptyContentList::Item(uint32_t aIndex)
169
0
{
170
0
  return nullptr;
171
0
}
172
173
// Hashtable for storing nsContentLists
174
static PLDHashTable* gContentListHashTable;
175
176
struct ContentListCache :
177
  public MruCache<nsContentListKey, nsContentList*, ContentListCache>
178
{
179
  static HashNumber Hash(const nsContentListKey& aKey)
180
0
  {
181
0
    return aKey.GetHash();
182
0
  }
183
  static bool Match(const nsContentListKey& aKey, const nsContentList* aVal)
184
0
  {
185
0
    return aVal->MatchesKey(aKey);
186
0
  }
187
};
188
189
static ContentListCache sRecentlyUsedContentLists;
190
191
struct ContentListHashEntry : public PLDHashEntryHdr
192
{
193
  nsContentList* mContentList;
194
};
195
196
static PLDHashNumber
197
ContentListHashtableHashKey(const void *key)
198
0
{
199
0
  const nsContentListKey* list = static_cast<const nsContentListKey *>(key);
200
0
  return list->GetHash();
201
0
}
202
203
static bool
204
ContentListHashtableMatchEntry(const PLDHashEntryHdr *entry, const void *key)
205
0
{
206
0
  const ContentListHashEntry *e =
207
0
    static_cast<const ContentListHashEntry *>(entry);
208
0
  const nsContentList* list = e->mContentList;
209
0
  const nsContentListKey* ourKey = static_cast<const nsContentListKey *>(key);
210
0
211
0
  return list->MatchesKey(*ourKey);
212
0
}
213
214
already_AddRefed<nsContentList>
215
NS_GetContentList(nsINode* aRootNode,
216
                  int32_t  aMatchNameSpaceId,
217
                  const nsAString& aTagname)
218
0
{
219
0
  NS_ASSERTION(aRootNode, "content list has to have a root");
220
0
221
0
  RefPtr<nsContentList> list;
222
0
  nsContentListKey hashKey(aRootNode, aMatchNameSpaceId, aTagname,
223
0
                           aRootNode->OwnerDoc()->IsHTMLDocument());
224
0
  auto p = sRecentlyUsedContentLists.Lookup(hashKey);
225
0
  if (p) {
226
0
    list = p.Data();
227
0
    return list.forget();
228
0
  }
229
0
230
0
  static const PLDHashTableOps hash_table_ops =
231
0
  {
232
0
    ContentListHashtableHashKey,
233
0
    ContentListHashtableMatchEntry,
234
0
    PLDHashTable::MoveEntryStub,
235
0
    PLDHashTable::ClearEntryStub
236
0
  };
237
0
238
0
  // Initialize the hashtable if needed.
239
0
  if (!gContentListHashTable) {
240
0
    gContentListHashTable =
241
0
      new PLDHashTable(&hash_table_ops, sizeof(ContentListHashEntry));
242
0
  }
243
0
244
0
  // First we look in our hashtable.  Then we create a content list if needed
245
0
  auto entry = static_cast<ContentListHashEntry*>
246
0
                          (gContentListHashTable->Add(&hashKey, fallible));
247
0
  if (entry)
248
0
    list = entry->mContentList;
249
0
250
0
  if (!list) {
251
0
    // We need to create a ContentList and add it to our new entry, if
252
0
    // we have an entry
253
0
    RefPtr<nsAtom> xmlAtom = NS_Atomize(aTagname);
254
0
    RefPtr<nsAtom> htmlAtom;
255
0
    if (aMatchNameSpaceId == kNameSpaceID_Unknown) {
256
0
      nsAutoString lowercaseName;
257
0
      nsContentUtils::ASCIIToLower(aTagname, lowercaseName);
258
0
      htmlAtom = NS_Atomize(lowercaseName);
259
0
    } else {
260
0
      htmlAtom = xmlAtom;
261
0
    }
262
0
    list = new nsContentList(aRootNode, aMatchNameSpaceId, htmlAtom, xmlAtom);
263
0
    if (entry) {
264
0
      entry->mContentList = list;
265
0
    }
266
0
  }
267
0
268
0
  p.Set(list);
269
0
  return list.forget();
270
0
}
271
272
#ifdef DEBUG
273
const nsCacheableFuncStringContentList::ContentListType
274
  nsCachableElementsByNameNodeList::sType = nsCacheableFuncStringContentList::eNodeList;
275
const nsCacheableFuncStringContentList::ContentListType
276
  nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
277
#endif
278
279
// Hashtable for storing nsCacheableFuncStringContentList
280
static PLDHashTable* gFuncStringContentListHashTable;
281
282
struct FuncStringContentListHashEntry : public PLDHashEntryHdr
283
{
284
  nsCacheableFuncStringContentList* mContentList;
285
};
286
287
static PLDHashNumber
288
FuncStringContentListHashtableHashKey(const void *key)
289
0
{
290
0
  const nsFuncStringCacheKey* funcStringKey =
291
0
    static_cast<const nsFuncStringCacheKey *>(key);
292
0
  return funcStringKey->GetHash();
293
0
}
294
295
static bool
296
FuncStringContentListHashtableMatchEntry(const PLDHashEntryHdr *entry,
297
                                         const void *key)
298
0
{
299
0
  const FuncStringContentListHashEntry *e =
300
0
    static_cast<const FuncStringContentListHashEntry *>(entry);
301
0
  const nsFuncStringCacheKey* ourKey =
302
0
    static_cast<const nsFuncStringCacheKey *>(key);
303
0
304
0
  return e->mContentList->Equals(ourKey);
305
0
}
306
307
template<class ListType>
308
already_AddRefed<nsContentList>
309
GetFuncStringContentList(nsINode* aRootNode,
310
                         nsContentListMatchFunc aFunc,
311
                         nsContentListDestroyFunc aDestroyFunc,
312
                         nsFuncStringContentListDataAllocator aDataAllocator,
313
                         const nsAString& aString)
314
0
{
315
0
  NS_ASSERTION(aRootNode, "content list has to have a root");
316
0
317
0
  RefPtr<nsCacheableFuncStringContentList> list;
318
0
319
0
  static const PLDHashTableOps hash_table_ops =
320
0
  {
321
0
    FuncStringContentListHashtableHashKey,
322
0
    FuncStringContentListHashtableMatchEntry,
323
0
    PLDHashTable::MoveEntryStub,
324
0
    PLDHashTable::ClearEntryStub
325
0
  };
326
0
327
0
  // Initialize the hashtable if needed.
328
0
  if (!gFuncStringContentListHashTable) {
329
0
    gFuncStringContentListHashTable =
330
0
      new PLDHashTable(&hash_table_ops, sizeof(FuncStringContentListHashEntry));
331
0
  }
332
0
333
0
  FuncStringContentListHashEntry *entry = nullptr;
334
0
  // First we look in our hashtable.  Then we create a content list if needed
335
0
  if (gFuncStringContentListHashTable) {
336
0
    nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
337
0
338
0
    entry = static_cast<FuncStringContentListHashEntry*>
339
0
      (gFuncStringContentListHashTable->Add(&hashKey, fallible));
340
0
    if (entry) {
341
0
      list = entry->mContentList;
342
#ifdef DEBUG
343
      MOZ_ASSERT_IF(list, list->mType == ListType::sType);
344
#endif
345
    }
346
0
  }
347
0
348
0
  if (!list) {
349
0
    // We need to create a ContentList and add it to our new entry, if
350
0
    // we have an entry
351
0
    list = new ListType(aRootNode, aFunc, aDestroyFunc, aDataAllocator,
352
0
                        aString);
353
0
    if (entry) {
354
0
      entry->mContentList = list;
355
0
    }
356
0
  }
357
0
358
0
  // Don't cache these lists globally
359
0
360
0
  return list.forget();
361
0
}
Unexecuted instantiation: already_AddRefed<nsContentList> GetFuncStringContentList<nsCachableElementsByNameNodeList>(nsINode*, bool (*)(mozilla::dom::Element*, int, nsAtom*, void*), void (*)(void*), void* (*)(nsINode*, nsTString<char16_t> const*), nsTSubstring<char16_t> const&)
Unexecuted instantiation: already_AddRefed<nsContentList> GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(nsINode*, bool (*)(mozilla::dom::Element*, int, nsAtom*, void*), void (*)(void*), void* (*)(nsINode*, nsTString<char16_t> const*), nsTSubstring<char16_t> const&)
362
363
// Explicit instantiations to avoid link errors
364
template
365
already_AddRefed<nsContentList>
366
GetFuncStringContentList<nsCachableElementsByNameNodeList>(nsINode* aRootNode,
367
                                                           nsContentListMatchFunc aFunc,
368
                                                           nsContentListDestroyFunc aDestroyFunc,
369
                                                           nsFuncStringContentListDataAllocator aDataAllocator,
370
                                                           const nsAString& aString);
371
template
372
already_AddRefed<nsContentList>
373
GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(nsINode* aRootNode,
374
                                                              nsContentListMatchFunc aFunc,
375
                                                              nsContentListDestroyFunc aDestroyFunc,
376
                                                              nsFuncStringContentListDataAllocator aDataAllocator,
377
                                                              const nsAString& aString);
378
379
//-----------------------------------------------------
380
// nsContentList implementation
381
382
nsContentList::nsContentList(nsINode* aRootNode,
383
                             int32_t aMatchNameSpaceId,
384
                             nsAtom* aHTMLMatchAtom,
385
                             nsAtom* aXMLMatchAtom,
386
                             bool aDeep,
387
                             bool aLiveList)
388
  : nsBaseContentList(),
389
    mRootNode(aRootNode),
390
    mMatchNameSpaceId(aMatchNameSpaceId),
391
    mHTMLMatchAtom(aHTMLMatchAtom),
392
    mXMLMatchAtom(aXMLMatchAtom),
393
    mFunc(nullptr),
394
    mDestroyFunc(nullptr),
395
    mData(nullptr),
396
    mState(LIST_DIRTY),
397
    mDeep(aDeep),
398
    mFuncMayDependOnAttr(false),
399
    mIsHTMLDocument(aRootNode->OwnerDoc()->IsHTMLDocument()),
400
    mIsLiveList(aLiveList)
401
0
{
402
0
  NS_ASSERTION(mRootNode, "Must have root");
403
0
  if (nsGkAtoms::_asterisk == mHTMLMatchAtom) {
404
0
    NS_ASSERTION(mXMLMatchAtom == nsGkAtoms::_asterisk, "HTML atom and XML atom are not both asterisk?");
405
0
    mMatchAll = true;
406
0
  }
407
0
  else {
408
0
    mMatchAll = false;
409
0
  }
410
0
  if (mIsLiveList) {
411
0
    mRootNode->AddMutationObserver(this);
412
0
  }
413
0
414
0
  // We only need to flush if we're in an non-HTML document, since the
415
0
  // HTML5 parser doesn't need flushing.  Further, if we're not in a
416
0
  // document at all right now (in the GetUncomposedDoc() sense), we're
417
0
  // not parser-created and don't need to be flushing stuff under us
418
0
  // to get our kids right.
419
0
  nsIDocument* doc = mRootNode->GetUncomposedDoc();
420
0
  mFlushesNeeded = doc && !doc->IsHTMLDocument();
421
0
}
422
423
nsContentList::nsContentList(nsINode* aRootNode,
424
                             nsContentListMatchFunc aFunc,
425
                             nsContentListDestroyFunc aDestroyFunc,
426
                             void* aData,
427
                             bool aDeep,
428
                             nsAtom* aMatchAtom,
429
                             int32_t aMatchNameSpaceId,
430
                             bool aFuncMayDependOnAttr,
431
                             bool aLiveList)
432
  : nsBaseContentList(),
433
    mRootNode(aRootNode),
434
    mMatchNameSpaceId(aMatchNameSpaceId),
435
    mHTMLMatchAtom(aMatchAtom),
436
    mXMLMatchAtom(aMatchAtom),
437
    mFunc(aFunc),
438
    mDestroyFunc(aDestroyFunc),
439
    mData(aData),
440
    mState(LIST_DIRTY),
441
    mMatchAll(false),
442
    mDeep(aDeep),
443
    mFuncMayDependOnAttr(aFuncMayDependOnAttr),
444
    mIsHTMLDocument(false),
445
    mIsLiveList(aLiveList)
446
0
{
447
0
  NS_ASSERTION(mRootNode, "Must have root");
448
0
  if (mIsLiveList) {
449
0
    mRootNode->AddMutationObserver(this);
450
0
  }
451
0
452
0
  // We only need to flush if we're in an non-HTML document, since the
453
0
  // HTML5 parser doesn't need flushing.  Further, if we're not in a
454
0
  // document at all right now (in the GetUncomposedDoc() sense), we're
455
0
  // not parser-created and don't need to be flushing stuff under us
456
0
  // to get our kids right.
457
0
  nsIDocument* doc = mRootNode->GetUncomposedDoc();
458
0
  mFlushesNeeded = doc && !doc->IsHTMLDocument();
459
0
}
460
461
nsContentList::~nsContentList()
462
0
{
463
0
  RemoveFromHashtable();
464
0
  if (mIsLiveList && mRootNode) {
465
0
    mRootNode->RemoveMutationObserver(this);
466
0
  }
467
0
468
0
  if (mDestroyFunc) {
469
0
    // Clean up mData
470
0
    (*mDestroyFunc)(mData);
471
0
  }
472
0
}
473
474
JSObject*
475
nsContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
476
0
{
477
0
  return HTMLCollection_Binding::Wrap(cx, this, aGivenProto);
478
0
}
479
480
NS_IMPL_ISUPPORTS_INHERITED(nsContentList, nsBaseContentList,
481
                            nsIHTMLCollection, nsIMutationObserver)
482
483
uint32_t
484
nsContentList::Length(bool aDoFlush)
485
0
{
486
0
  BringSelfUpToDate(aDoFlush);
487
0
488
0
  return mElements.Length();
489
0
}
490
491
nsIContent *
492
nsContentList::Item(uint32_t aIndex, bool aDoFlush)
493
0
{
494
0
  if (mRootNode && aDoFlush && mFlushesNeeded) {
495
0
    // XXX sXBL/XBL2 issue
496
0
    nsIDocument* doc = mRootNode->GetUncomposedDoc();
497
0
    if (doc) {
498
0
      // Flush pending content changes Bug 4891.
499
0
      doc->FlushPendingNotifications(FlushType::ContentAndNotify);
500
0
    }
501
0
  }
502
0
503
0
  if (mState != LIST_UP_TO_DATE)
504
0
    PopulateSelf(std::min(aIndex, UINT32_MAX - 1) + 1);
505
0
506
0
  ASSERT_IN_SYNC;
507
0
  NS_ASSERTION(!mRootNode || mState != LIST_DIRTY,
508
0
               "PopulateSelf left the list in a dirty (useless) state!");
509
0
510
0
  return mElements.SafeElementAt(aIndex);
511
0
}
512
513
Element*
514
nsContentList::NamedItem(const nsAString& aName, bool aDoFlush)
515
0
{
516
0
  if (aName.IsEmpty()) {
517
0
    return nullptr;
518
0
  }
519
0
520
0
  BringSelfUpToDate(aDoFlush);
521
0
522
0
  uint32_t i, count = mElements.Length();
523
0
524
0
  // Typically IDs and names are atomized
525
0
  RefPtr<nsAtom> name = NS_Atomize(aName);
526
0
  NS_ENSURE_TRUE(name, nullptr);
527
0
528
0
  for (i = 0; i < count; i++) {
529
0
    nsIContent *content = mElements[i];
530
0
    // XXX Should this pass eIgnoreCase?
531
0
    if (content &&
532
0
        ((content->IsHTMLElement() &&
533
0
          content->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
534
0
                                            name, eCaseMatters)) ||
535
0
         content->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
536
0
                                           name, eCaseMatters))) {
537
0
      return content->AsElement();
538
0
    }
539
0
  }
540
0
541
0
  return nullptr;
542
0
}
543
544
void
545
nsContentList::GetSupportedNames(nsTArray<nsString>& aNames)
546
0
{
547
0
  BringSelfUpToDate(true);
548
0
549
0
  AutoTArray<nsAtom*, 8> atoms;
550
0
  for (uint32_t i = 0; i < mElements.Length(); ++i) {
551
0
    nsIContent *content = mElements.ElementAt(i);
552
0
    if (content->HasID()) {
553
0
      nsAtom* id = content->GetID();
554
0
      MOZ_ASSERT(id != nsGkAtoms::_empty,
555
0
                 "Empty ids don't get atomized");
556
0
      if (!atoms.Contains(id)) {
557
0
        atoms.AppendElement(id);
558
0
      }
559
0
    }
560
0
561
0
    nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(content);
562
0
    if (el) {
563
0
      // XXXbz should we be checking for particular tags here?  How
564
0
      // stable is this part of the spec?
565
0
      // Note: nsINode::HasName means the name is exposed on the document,
566
0
      // which is false for options, so we don't check it here.
567
0
      const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
568
0
      if (val && val->Type() == nsAttrValue::eAtom) {
569
0
        nsAtom* name = val->GetAtomValue();
570
0
        MOZ_ASSERT(name != nsGkAtoms::_empty,
571
0
                   "Empty names don't get atomized");
572
0
        if (!atoms.Contains(name)) {
573
0
          atoms.AppendElement(name);
574
0
        }
575
0
      }
576
0
    }
577
0
  }
578
0
579
0
  uint32_t atomsLen = atoms.Length();
580
0
  nsString* names = aNames.AppendElements(atomsLen);
581
0
  for (uint32_t i = 0; i < atomsLen; ++i) {
582
0
    atoms[i]->ToString(names[i]);
583
0
  }
584
0
}
585
586
int32_t
587
nsContentList::IndexOf(nsIContent *aContent, bool aDoFlush)
588
0
{
589
0
  BringSelfUpToDate(aDoFlush);
590
0
591
0
  return mElements.IndexOf(aContent);
592
0
}
593
594
int32_t
595
nsContentList::IndexOf(nsIContent* aContent)
596
0
{
597
0
  return IndexOf(aContent, true);
598
0
}
599
600
void
601
nsContentList::NodeWillBeDestroyed(const nsINode* aNode)
602
0
{
603
0
  // We shouldn't do anything useful from now on
604
0
605
0
  RemoveFromCaches();
606
0
  mRootNode = nullptr;
607
0
608
0
  // We will get no more updates, so we can never know we're up to
609
0
  // date
610
0
  SetDirty();
611
0
}
612
613
void
614
nsContentList::LastRelease()
615
0
{
616
0
  RemoveFromCaches();
617
0
  if (mIsLiveList && mRootNode) {
618
0
    mRootNode->RemoveMutationObserver(this);
619
0
    mRootNode = nullptr;
620
0
  }
621
0
  SetDirty();
622
0
}
623
624
Element*
625
nsContentList::GetElementAt(uint32_t aIndex)
626
0
{
627
0
  return static_cast<Element*>(Item(aIndex, true));
628
0
}
629
630
nsIContent*
631
nsContentList::Item(uint32_t aIndex)
632
0
{
633
0
  return GetElementAt(aIndex);
634
0
}
635
636
void
637
nsContentList::AttributeChanged(Element* aElement,
638
                                int32_t aNameSpaceID,
639
                                nsAtom* aAttribute,
640
                                int32_t aModType,
641
                                const nsAttrValue* aOldValue)
642
0
{
643
0
  MOZ_ASSERT(aElement, "Must have a content node to work with");
644
0
645
0
  if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
646
0
      !MayContainRelevantNodes(aElement->GetParentNode()) ||
647
0
      !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
648
0
    // Either we're already dirty or this notification doesn't affect
649
0
    // whether we might match aElement.
650
0
    return;
651
0
  }
652
0
653
0
  if (Match(aElement)) {
654
0
    if (mElements.IndexOf(aElement) == mElements.NoIndex) {
655
0
      // We match aElement now, and it's not in our list already.  Just dirty
656
0
      // ourselves; this is simpler than trying to figure out where to insert
657
0
      // aElement.
658
0
      SetDirty();
659
0
    }
660
0
  } else {
661
0
    // We no longer match aElement.  Remove it from our list.  If it's
662
0
    // already not there, this is a no-op (though a potentially
663
0
    // expensive one).  Either way, no change of mState is required
664
0
    // here.
665
0
    mElements.RemoveElement(aElement);
666
0
  }
667
0
}
668
669
void
670
nsContentList::ContentAppended(nsIContent* aFirstNewContent)
671
0
{
672
0
  nsIContent* container = aFirstNewContent->GetParent();
673
0
  MOZ_ASSERT(container, "Can't get at the new content if no container!");
674
0
675
0
  /*
676
0
   * If the state is LIST_DIRTY then we have no useful information in our list
677
0
   * and we want to put off doing work as much as possible.
678
0
   *
679
0
   * Also, if container is anonymous from our point of view, we know that we
680
0
   * can't possibly be matching any of the kids.
681
0
   *
682
0
   * Optimize out also the common case when just one new node is appended and
683
0
   * it doesn't match us.
684
0
   */
685
0
  if (mState == LIST_DIRTY ||
686
0
      !nsContentUtils::IsInSameAnonymousTree(mRootNode, container) ||
687
0
      !MayContainRelevantNodes(container) ||
688
0
      (!aFirstNewContent->HasChildren() &&
689
0
       !aFirstNewContent->GetNextSibling() &&
690
0
       !MatchSelf(aFirstNewContent))) {
691
0
    return;
692
0
  }
693
0
694
0
  /*
695
0
   * We want to handle the case of ContentAppended by sometimes
696
0
   * appending the content to our list, not just setting state to
697
0
   * LIST_DIRTY, since most of our ContentAppended notifications
698
0
   * should come during pageload and be at the end of the document.
699
0
   * Do a bit of work to see whether we could just append to what we
700
0
   * already have.
701
0
   */
702
0
703
0
  int32_t count = container->GetChildCount();
704
0
705
0
  if (count > 0) {
706
0
    uint32_t ourCount = mElements.Length();
707
0
    bool appendToList = false;
708
0
    if (ourCount == 0) {
709
0
      appendToList = true;
710
0
    } else {
711
0
      nsIContent* ourLastContent = mElements[ourCount - 1];
712
0
      /*
713
0
       * We want to append instead of invalidating if the first thing
714
0
       * that got appended comes after ourLastContent.
715
0
       */
716
0
      if (nsContentUtils::PositionIsBefore(ourLastContent, aFirstNewContent)) {
717
0
        appendToList = true;
718
0
      }
719
0
    }
720
0
721
0
722
0
    if (!appendToList) {
723
0
      // The new stuff is somewhere in the middle of our list; check
724
0
      // whether we need to invalidate
725
0
      for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
726
0
        if (MatchSelf(cur)) {
727
0
          // Uh-oh.  We're gonna have to add elements into the middle
728
0
          // of our list. That's not worth the effort.
729
0
          SetDirty();
730
0
          break;
731
0
        }
732
0
      }
733
0
734
0
      ASSERT_IN_SYNC;
735
0
      return;
736
0
    }
737
0
738
0
    /*
739
0
     * At this point we know we could append.  If we're not up to
740
0
     * date, however, that would be a bad idea -- it could miss some
741
0
     * content that we never picked up due to being lazy.  Further, we
742
0
     * may never get asked for this content... so don't grab it yet.
743
0
     */
744
0
    if (mState == LIST_LAZY) // be lazy
745
0
      return;
746
0
747
0
    /*
748
0
     * We're up to date.  That means someone's actively using us; we
749
0
     * may as well grab this content....
750
0
     */
751
0
    if (mDeep) {
752
0
      for (nsIContent* cur = aFirstNewContent;
753
0
           cur;
754
0
           cur = cur->GetNextNode(container)) {
755
0
        if (cur->IsElement() && Match(cur->AsElement())) {
756
0
          mElements.AppendElement(cur);
757
0
        }
758
0
      }
759
0
    } else {
760
0
      for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
761
0
        if (cur->IsElement() && Match(cur->AsElement())) {
762
0
          mElements.AppendElement(cur);
763
0
        }
764
0
      }
765
0
    }
766
0
767
0
    ASSERT_IN_SYNC;
768
0
  }
769
0
}
770
771
void
772
nsContentList::ContentInserted(nsIContent* aChild)
773
0
{
774
0
  // Note that aChild->GetParentNode() can be null here if we are inserting into
775
0
  // the document itself; any attempted optimizations to this method should deal
776
0
  // with that.
777
0
  if (mState != LIST_DIRTY &&
778
0
      MayContainRelevantNodes(aChild->GetParentNode()) &&
779
0
      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
780
0
      MatchSelf(aChild)) {
781
0
    SetDirty();
782
0
  }
783
0
784
0
  ASSERT_IN_SYNC;
785
0
}
786
787
void
788
nsContentList::ContentRemoved(nsIContent* aChild,
789
                              nsIContent* aPreviousSibling)
790
0
{
791
0
  if (mState != LIST_DIRTY &&
792
0
      MayContainRelevantNodes(aChild->GetParentNode()) &&
793
0
      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
794
0
      MatchSelf(aChild)) {
795
0
    SetDirty();
796
0
  }
797
0
798
0
  ASSERT_IN_SYNC;
799
0
}
800
801
bool
802
nsContentList::Match(Element *aElement)
803
0
{
804
0
  if (mFunc) {
805
0
    return (*mFunc)(aElement, mMatchNameSpaceId, mXMLMatchAtom, mData);
806
0
  }
807
0
808
0
  if (!mXMLMatchAtom)
809
0
    return false;
810
0
811
0
  NodeInfo *ni = aElement->NodeInfo();
812
0
813
0
  bool unknown = mMatchNameSpaceId == kNameSpaceID_Unknown;
814
0
  bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard;
815
0
  bool toReturn = mMatchAll;
816
0
  if (!unknown && !wildcard)
817
0
    toReturn &= ni->NamespaceEquals(mMatchNameSpaceId);
818
0
819
0
  if (toReturn)
820
0
    return toReturn;
821
0
822
0
  bool matchHTML =
823
0
    mIsHTMLDocument && aElement->GetNameSpaceID() == kNameSpaceID_XHTML;
824
0
825
0
  if (unknown) {
826
0
    return matchHTML ? ni->QualifiedNameEquals(mHTMLMatchAtom) :
827
0
                       ni->QualifiedNameEquals(mXMLMatchAtom);
828
0
  }
829
0
830
0
  if (wildcard) {
831
0
    return matchHTML ? ni->Equals(mHTMLMatchAtom) :
832
0
                       ni->Equals(mXMLMatchAtom);
833
0
  }
834
0
835
0
  return matchHTML ? ni->Equals(mHTMLMatchAtom, mMatchNameSpaceId) :
836
0
                     ni->Equals(mXMLMatchAtom, mMatchNameSpaceId);
837
0
}
838
839
bool
840
nsContentList::MatchSelf(nsIContent *aContent)
841
0
{
842
0
  MOZ_ASSERT(aContent, "Can't match null stuff, you know");
843
0
  MOZ_ASSERT(mDeep || aContent->GetParentNode() == mRootNode,
844
0
             "MatchSelf called on a node that we can't possibly match");
845
0
846
0
  if (!aContent->IsElement()) {
847
0
    return false;
848
0
  }
849
0
850
0
  if (Match(aContent->AsElement()))
851
0
    return true;
852
0
853
0
  if (!mDeep)
854
0
    return false;
855
0
856
0
  for (nsIContent* cur = aContent->GetFirstChild();
857
0
       cur;
858
0
       cur = cur->GetNextNode(aContent)) {
859
0
    if (cur->IsElement() && Match(cur->AsElement())) {
860
0
      return true;
861
0
    }
862
0
  }
863
0
864
0
  return false;
865
0
}
866
867
void
868
nsContentList::PopulateSelf(uint32_t aNeededLength,
869
                            uint32_t aExpectedElementsIfDirty)
870
0
{
871
0
  if (!mRootNode) {
872
0
    return;
873
0
  }
874
0
875
0
  ASSERT_IN_SYNC;
876
0
877
0
  uint32_t count = mElements.Length();
878
0
  NS_ASSERTION(mState != LIST_DIRTY || count == aExpectedElementsIfDirty,
879
0
               "Reset() not called when setting state to LIST_DIRTY?");
880
0
881
0
  if (count >= aNeededLength) // We're all set
882
0
    return;
883
0
884
0
  uint32_t elementsToAppend = aNeededLength - count;
885
#ifdef DEBUG
886
  uint32_t invariant = elementsToAppend + mElements.Length();
887
#endif
888
889
0
  if (mDeep) {
890
0
    // If we already have nodes start searching at the last one, otherwise
891
0
    // start searching at the root.
892
0
    nsINode* cur = count ? mElements[count - 1] : mRootNode;
893
0
    do {
894
0
      cur = cur->GetNextNode(mRootNode);
895
0
      if (!cur) {
896
0
        break;
897
0
      }
898
0
      if (cur->IsElement() && Match(cur->AsElement())) {
899
0
        // Append AsElement() to get nsIContent instead of nsINode
900
0
        mElements.AppendElement(cur->AsElement());
901
0
        --elementsToAppend;
902
0
      }
903
0
    } while (elementsToAppend);
904
0
  } else {
905
0
    nsIContent* cur =
906
0
      count ? mElements[count-1]->GetNextSibling() : mRootNode->GetFirstChild();
907
0
    for ( ; cur && elementsToAppend; cur = cur->GetNextSibling()) {
908
0
      if (cur->IsElement() && Match(cur->AsElement())) {
909
0
        mElements.AppendElement(cur);
910
0
        --elementsToAppend;
911
0
      }
912
0
    }
913
0
  }
914
0
915
0
  NS_ASSERTION(elementsToAppend + mElements.Length() == invariant,
916
0
               "Something is awry!");
917
0
918
0
  if (elementsToAppend != 0)
919
0
    mState = LIST_UP_TO_DATE;
920
0
  else
921
0
    mState = LIST_LAZY;
922
0
923
0
  ASSERT_IN_SYNC;
924
0
}
925
926
void
927
nsContentList::RemoveFromHashtable()
928
0
{
929
0
  if (mFunc) {
930
0
    // This can't be in the table anyway
931
0
    return;
932
0
  }
933
0
934
0
  nsDependentAtomString str(mXMLMatchAtom);
935
0
  nsContentListKey key(mRootNode, mMatchNameSpaceId, str, mIsHTMLDocument);
936
0
  sRecentlyUsedContentLists.Remove(key);
937
0
938
0
  if (!gContentListHashTable)
939
0
    return;
940
0
941
0
  gContentListHashTable->Remove(&key);
942
0
943
0
  if (gContentListHashTable->EntryCount() == 0) {
944
0
    delete gContentListHashTable;
945
0
    gContentListHashTable = nullptr;
946
0
  }
947
0
}
948
949
void
950
nsContentList::BringSelfUpToDate(bool aDoFlush)
951
0
{
952
0
  if (mRootNode && aDoFlush && mFlushesNeeded) {
953
0
    // XXX sXBL/XBL2 issue
954
0
    nsIDocument* doc = mRootNode->GetUncomposedDoc();
955
0
    if (doc) {
956
0
      // Flush pending content changes Bug 4891.
957
0
      doc->FlushPendingNotifications(FlushType::ContentAndNotify);
958
0
    }
959
0
  }
960
0
961
0
  if (mState != LIST_UP_TO_DATE)
962
0
    PopulateSelf(uint32_t(-1));
963
0
964
0
  ASSERT_IN_SYNC;
965
0
  NS_ASSERTION(!mRootNode || mState == LIST_UP_TO_DATE,
966
0
               "PopulateSelf dod not bring content list up to date!");
967
0
}
968
969
nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList()
970
0
{
971
0
  RemoveFromFuncStringHashtable();
972
0
}
973
974
void
975
nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable()
976
0
{
977
0
  if (!gFuncStringContentListHashTable) {
978
0
    return;
979
0
  }
980
0
981
0
  nsFuncStringCacheKey key(mRootNode, mFunc, mString);
982
0
  gFuncStringContentListHashTable->Remove(&key);
983
0
984
0
  if (gFuncStringContentListHashTable->EntryCount() == 0) {
985
0
    delete gFuncStringContentListHashTable;
986
0
    gFuncStringContentListHashTable = nullptr;
987
0
  }
988
0
}
989
990
#ifdef DEBUG_CONTENT_LIST
991
void
992
nsContentList::AssertInSync()
993
{
994
  if (mState == LIST_DIRTY) {
995
    return;
996
  }
997
998
  if (!mRootNode) {
999
    NS_ASSERTION(mElements.Length() == 0 && mState == LIST_DIRTY,
1000
                 "Empty iterator isn't quite empty?");
1001
    return;
1002
  }
1003
1004
  // XXX This code will need to change if nsContentLists can ever match
1005
  // elements that are outside of the document element.
1006
  nsIContent* root = mRootNode->IsDocument()
1007
    ? mRootNode->AsDocument()->GetRootElement()
1008
    : mRootNode->AsContent();
1009
1010
  nsCOMPtr<nsIContentIterator> iter;
1011
  if (mDeep) {
1012
    iter = NS_NewPreContentIterator();
1013
    iter->Init(root);
1014
    iter->First();
1015
  }
1016
1017
  uint32_t cnt = 0, index = 0;
1018
  while (true) {
1019
    if (cnt == mElements.Length() && mState == LIST_LAZY) {
1020
      break;
1021
    }
1022
1023
    nsIContent *cur = mDeep ? iter->GetCurrentNode() :
1024
                              mRootNode->GetChildAt(index++);
1025
    if (!cur) {
1026
      break;
1027
    }
1028
1029
    if (cur->IsElement() && Match(cur->AsElement())) {
1030
      NS_ASSERTION(cnt < mElements.Length() && mElements[cnt] == cur,
1031
                   "Elements is out of sync");
1032
      ++cnt;
1033
    }
1034
1035
    if (mDeep) {
1036
      iter->Next();
1037
    }
1038
  }
1039
1040
  NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
1041
}
1042
#endif
1043
1044
//-----------------------------------------------------
1045
// nsCachableElementsByNameNodeList
1046
1047
JSObject*
1048
nsCachableElementsByNameNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
1049
0
{
1050
0
  return NodeList_Binding::Wrap(cx, this, aGivenProto);
1051
0
}
1052
1053
void
1054
nsCachableElementsByNameNodeList::AttributeChanged(Element* aElement,
1055
                                                   int32_t aNameSpaceID,
1056
                                                   nsAtom* aAttribute,
1057
                                                   int32_t aModType,
1058
                                                   const nsAttrValue* aOldValue)
1059
0
{
1060
0
  // No need to rebuild the list if the changed attribute is not the name
1061
0
  // attribute.
1062
0
  if (aAttribute != nsGkAtoms::name) {
1063
0
    return;
1064
0
  }
1065
0
1066
0
  nsCacheableFuncStringContentList::AttributeChanged(aElement,
1067
0
                                                     aNameSpaceID, aAttribute,
1068
0
                                                     aModType, aOldValue);
1069
0
}
1070
1071
//-----------------------------------------------------
1072
// nsCacheableFuncStringHTMLCollection
1073
1074
JSObject*
1075
nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
1076
0
{
1077
0
  return HTMLCollection_Binding::Wrap(cx, this, aGivenProto);
1078
0
}
1079
1080
//-----------------------------------------------------
1081
// nsLabelsNodeList
1082
1083
JSObject*
1084
nsLabelsNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
1085
0
{
1086
0
  return NodeList_Binding::Wrap(cx, this, aGivenProto);
1087
0
}
1088
1089
void
1090
nsLabelsNodeList::AttributeChanged(Element* aElement,
1091
                                   int32_t aNameSpaceID, nsAtom* aAttribute,
1092
                                   int32_t aModType,
1093
                                   const nsAttrValue* aOldValue)
1094
0
{
1095
0
  MOZ_ASSERT(aElement, "Must have a content node to work with");
1096
0
  if (mState == LIST_DIRTY ||
1097
0
      !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
1098
0
    return;
1099
0
  }
1100
0
1101
0
  // We need to handle input type changes to or from "hidden".
1102
0
  if (aElement->IsHTMLElement(nsGkAtoms::input) &&
1103
0
      aAttribute == nsGkAtoms::type && aNameSpaceID == kNameSpaceID_None) {
1104
0
    SetDirty();
1105
0
    return;
1106
0
  }
1107
0
}
1108
1109
void
1110
nsLabelsNodeList::ContentAppended(nsIContent* aFirstNewContent)
1111
0
{
1112
0
  nsIContent* container = aFirstNewContent->GetParent();
1113
0
  // If a labelable element is moved to outside or inside of
1114
0
  // nested associated labels, we're gonna have to modify
1115
0
  // the content list.
1116
0
  if (mState != LIST_DIRTY ||
1117
0
      nsContentUtils::IsInSameAnonymousTree(mRootNode, container)) {
1118
0
    SetDirty();
1119
0
    return;
1120
0
  }
1121
0
}
1122
1123
void
1124
nsLabelsNodeList::ContentInserted(nsIContent* aChild)
1125
0
{
1126
0
  // If a labelable element is moved to outside or inside of
1127
0
  // nested associated labels, we're gonna have to modify
1128
0
  // the content list.
1129
0
  if (mState != LIST_DIRTY ||
1130
0
      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
1131
0
    SetDirty();
1132
0
    return;
1133
0
  }
1134
0
}
1135
1136
void
1137
nsLabelsNodeList::ContentRemoved(nsIContent* aChild,
1138
                                 nsIContent* aPreviousSibling)
1139
0
{
1140
0
  // If a labelable element is removed, we're gonna have to clean
1141
0
  // the content list.
1142
0
  if (mState != LIST_DIRTY ||
1143
0
      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
1144
0
    SetDirty();
1145
0
    return;
1146
0
  }
1147
0
}
1148
1149
void
1150
nsLabelsNodeList::MaybeResetRoot(nsINode* aRootNode)
1151
0
{
1152
0
  MOZ_ASSERT(aRootNode, "Must have root");
1153
0
  if (mRootNode == aRootNode) {
1154
0
    return;
1155
0
  }
1156
0
1157
0
  MOZ_ASSERT(mIsLiveList, "nsLabelsNodeList is always a live list");
1158
0
  if (mRootNode) {
1159
0
    mRootNode->RemoveMutationObserver(this);
1160
0
  }
1161
0
  mRootNode = aRootNode;
1162
0
  mRootNode->AddMutationObserver(this);
1163
0
  SetDirty();
1164
0
}
1165
1166
void
1167
nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength,
1168
                               uint32_t aExpectedElementsIfDirty)
1169
0
{
1170
0
  if (!mRootNode) {
1171
0
    return;
1172
0
  }
1173
0
1174
0
  // Start searching at the root.
1175
0
  nsINode* cur = mRootNode;
1176
0
  if (mElements.IsEmpty() && cur->IsElement() && Match(cur->AsElement())) {
1177
0
    mElements.AppendElement(cur->AsElement());
1178
0
    ++aExpectedElementsIfDirty;
1179
0
  }
1180
0
1181
0
  nsContentList::PopulateSelf(aNeededLength, aExpectedElementsIfDirty);
1182
0
}