Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/HTMLTableElement.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 "mozilla/dom/HTMLTableElement.h"
8
#include "mozilla/MappedDeclarations.h"
9
#include "nsAttrValueInlines.h"
10
#include "nsHTMLStyleSheet.h"
11
#include "nsMappedAttributes.h"
12
#include "mozilla/dom/HTMLCollectionBinding.h"
13
#include "mozilla/dom/HTMLTableElementBinding.h"
14
#include "nsContentUtils.h"
15
#include "jsfriendapi.h"
16
17
NS_IMPL_NS_NEW_HTML_ELEMENT(Table)
18
19
namespace mozilla {
20
namespace dom {
21
22
/* ------------------------------ TableRowsCollection -------------------------------- */
23
/**
24
 * This class provides a late-bound collection of rows in a table.
25
 * mParent is NOT ref-counted to avoid circular references
26
 */
27
class TableRowsCollection final : public nsIHTMLCollection
28
                                , public nsStubMutationObserver
29
                                , public nsWrapperCache
30
{
31
public:
32
  explicit TableRowsCollection(HTMLTableElement* aParent);
33
34
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
35
36
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
37
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
38
  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
39
  NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
40
41
  virtual uint32_t Length() override;
42
  virtual Element* GetElementAt(uint32_t aIndex) override;
43
  virtual nsINode* GetParentObject() override
44
0
  {
45
0
    return mParent;
46
0
  }
47
48
  virtual Element*
49
  GetFirstNamedElement(const nsAString& aName, bool& aFound) override;
50
  virtual void GetSupportedNames(nsTArray<nsString>& aNames) override;
51
52
  NS_IMETHOD    ParentDestroyed();
53
54
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(TableRowsCollection, nsIHTMLCollection)
55
56
  // nsWrapperCache
57
  using nsWrapperCache::GetWrapperPreserveColor;
58
  using nsWrapperCache::PreserveWrapper;
59
  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
60
protected:
61
  // Unregister ourselves as a mutation observer, and clear our internal state.
62
  void CleanUp();
63
  void LastRelease()
64
0
  {
65
0
    CleanUp();
66
0
  }
67
  virtual ~TableRowsCollection()
68
0
  {
69
0
    // we do NOT have a ref-counted reference to mParent, so do NOT
70
0
    // release it!  this is to avoid circular references.  The
71
0
    // instantiator who provided mParent is responsible for managing our
72
0
    // reference for us.
73
0
    CleanUp();
74
0
  }
75
76
  virtual JSObject* GetWrapperPreserveColorInternal() override
77
0
  {
78
0
    return nsWrapperCache::GetWrapperPreserveColor();
79
0
  }
80
  virtual void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override
81
0
  {
82
0
    nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
83
0
  }
84
85
  // Ensure that HTMLTableElement is in a valid state. This must be called
86
  // before inspecting the mRows object.
87
  void EnsureInitialized();
88
89
  // Checks if the passed-in container is interesting for the purposes of
90
  // invalidation due to a mutation observer.
91
  bool InterestingContainer(nsIContent* aContainer);
92
93
  // Check if the passed-in nsIContent is a <tr> within the section defined by
94
  // `aSection`. The root of the table is considered to be part of the `<tbody>`
95
  // section.
96
  bool IsAppropriateRow(nsAtom* aSection, nsIContent* aContent);
97
98
  // Scan backwards starting from `aCurrent` in the table, looking for the
99
  // previous row in the table which is within the section `aSection`.
100
  nsIContent* PreviousRow(nsAtom* aSection, nsIContent* aCurrent);
101
102
  // Handle the insertion of the child `aChild` into the container `aContainer`
103
  // within the tree. The container must be an `InterestingContainer`. This
104
  // method updates the mRows, mBodyStart, and mFootStart member variables.
105
  //
106
  // HandleInsert returns an integer which can be passed to the next call of the
107
  // method in a loop inserting children into the same container. This will
108
  // optimize subsequent insertions to require less work. This can either be -1,
109
  // in which case we don't know where to insert the next row, and When passed
110
  // to HandleInsert, it will use `PreviousRow` to locate the index to insert.
111
  // Or, it can be an index to insert the next <tr> in the same container at.
112
  int32_t HandleInsert(nsIContent* aContainer,
113
                       nsIContent* aChild,
114
                       int32_t aIndexGuess = -1);
115
116
  // The HTMLTableElement which this TableRowsCollection tracks the rows for.
117
  HTMLTableElement* mParent;
118
119
  // The current state of the TableRowsCollection. mBodyStart and mFootStart are
120
  // indices into mRows which represent the location of the first row in the
121
  // body or foot section. If there are no rows in a section, the index points
122
  // at the location where the first element in that section would be inserted.
123
  nsTArray<nsCOMPtr<nsIContent>> mRows;
124
  uint32_t mBodyStart;
125
  uint32_t mFootStart;
126
  bool mInitialized;
127
};
128
129
130
TableRowsCollection::TableRowsCollection(HTMLTableElement *aParent)
131
  : mParent(aParent)
132
  , mBodyStart(0)
133
  , mFootStart(0)
134
  , mInitialized(false)
135
0
{
136
0
  MOZ_ASSERT(mParent);
137
0
}
138
139
void
140
TableRowsCollection::EnsureInitialized()
141
0
{
142
0
  if (mInitialized) {
143
0
    return;
144
0
  }
145
0
  mInitialized = true;
146
0
147
0
  // Initialize mRows as the TableRowsCollection is created. The mutation
148
0
  // observer should keep it up to date.
149
0
  //
150
0
  // It should be extremely unlikely that anyone creates a TableRowsCollection
151
0
  // without calling a method on it, so lazily performing this initialization
152
0
  // seems unnecessary.
153
0
  AutoTArray<nsCOMPtr<nsIContent>, 32> body;
154
0
  AutoTArray<nsCOMPtr<nsIContent>, 32> foot;
155
0
  mRows.Clear();
156
0
157
0
  auto addRowChildren = [&] (nsTArray<nsCOMPtr<nsIContent>>& aArray, nsIContent* aNode) {
158
0
    for (nsIContent* inner = aNode->nsINode::GetFirstChild();
159
0
         inner; inner = inner->GetNextSibling()) {
160
0
      if (inner->IsHTMLElement(nsGkAtoms::tr)) {
161
0
        aArray.AppendElement(inner);
162
0
      }
163
0
    }
164
0
  };
165
0
166
0
  for (nsIContent* node = mParent->nsINode::GetFirstChild();
167
0
       node; node = node->GetNextSibling()) {
168
0
    if (node->IsHTMLElement(nsGkAtoms::thead)) {
169
0
      addRowChildren(mRows, node);
170
0
    } else if (node->IsHTMLElement(nsGkAtoms::tbody)) {
171
0
      addRowChildren(body, node);
172
0
    } else if (node->IsHTMLElement(nsGkAtoms::tfoot)) {
173
0
      addRowChildren(foot, node);
174
0
    } else if (node->IsHTMLElement(nsGkAtoms::tr)) {
175
0
      body.AppendElement(node);
176
0
    }
177
0
  }
178
0
179
0
  mBodyStart = mRows.Length();
180
0
  mRows.AppendElements(std::move(body));
181
0
  mFootStart = mRows.Length();
182
0
  mRows.AppendElements(std::move(foot));
183
0
184
0
  mParent->AddMutationObserver(this);
185
0
}
186
187
void
188
TableRowsCollection::CleanUp()
189
0
{
190
0
  // Unregister ourselves as a mutation observer.
191
0
  if (mInitialized && mParent)  {
192
0
    mParent->RemoveMutationObserver(this);
193
0
  }
194
0
195
0
  // Clean up all of our internal state and make it empty in case someone looks
196
0
  // at us.
197
0
  mRows.Clear();
198
0
  mBodyStart = 0;
199
0
  mFootStart = 0;
200
0
201
0
  // We set mInitialized to true in case someone still has a reference to us, as
202
0
  // we don't need to try to initialize first.
203
0
  mInitialized = true;
204
0
  mParent = nullptr;
205
0
}
206
207
JSObject*
208
TableRowsCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
209
0
{
210
0
  return HTMLCollection_Binding::Wrap(aCx, this, aGivenProto);
211
0
}
212
213
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TableRowsCollection, mRows)
214
NS_IMPL_CYCLE_COLLECTING_ADDREF(TableRowsCollection)
215
NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(TableRowsCollection,
216
                                                   LastRelease())
217
218
0
NS_INTERFACE_TABLE_HEAD(TableRowsCollection)
219
0
  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
220
0
  NS_INTERFACE_TABLE(TableRowsCollection, nsIHTMLCollection, nsIMutationObserver)
221
0
  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(TableRowsCollection)
222
0
NS_INTERFACE_MAP_END
223
224
uint32_t
225
TableRowsCollection::Length()
226
0
{
227
0
  EnsureInitialized();
228
0
  return mRows.Length();
229
0
}
230
231
Element*
232
TableRowsCollection::GetElementAt(uint32_t aIndex)
233
0
{
234
0
  EnsureInitialized();
235
0
  if (aIndex < mRows.Length()) {
236
0
    return mRows[aIndex]->AsElement();
237
0
  }
238
0
  return nullptr;
239
0
}
240
241
Element*
242
TableRowsCollection::GetFirstNamedElement(const nsAString& aName, bool& aFound)
243
0
{
244
0
  EnsureInitialized();
245
0
  aFound = false;
246
0
  RefPtr<nsAtom> nameAtom = NS_Atomize(aName);
247
0
  NS_ENSURE_TRUE(nameAtom, nullptr);
248
0
249
0
  for (auto& node : mRows) {
250
0
    if (node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
251
0
                                       nameAtom, eCaseMatters) ||
252
0
        node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
253
0
                                       nameAtom, eCaseMatters)) {
254
0
      aFound = true;
255
0
      return node->AsElement();
256
0
    }
257
0
  }
258
0
259
0
  return nullptr;
260
0
}
261
262
void
263
TableRowsCollection::GetSupportedNames(nsTArray<nsString>& aNames)
264
0
{
265
0
  EnsureInitialized();
266
0
  for (auto& node : mRows) {
267
0
    if (node->HasID()) {
268
0
      nsAtom* idAtom = node->GetID();
269
0
      MOZ_ASSERT(idAtom != nsGkAtoms::_empty,
270
0
                 "Empty ids don't get atomized");
271
0
      nsDependentAtomString idStr(idAtom);
272
0
      if (!aNames.Contains(idStr)) {
273
0
        aNames.AppendElement(idStr);
274
0
      }
275
0
    }
276
0
277
0
    nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(node);
278
0
    if (el) {
279
0
      const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
280
0
      if (val && val->Type() == nsAttrValue::eAtom) {
281
0
        nsAtom* nameAtom = val->GetAtomValue();
282
0
        MOZ_ASSERT(nameAtom != nsGkAtoms::_empty,
283
0
                   "Empty names don't get atomized");
284
0
        nsDependentAtomString nameStr(nameAtom);
285
0
        if (!aNames.Contains(nameStr)) {
286
0
          aNames.AppendElement(nameStr);
287
0
        }
288
0
      }
289
0
    }
290
0
  }
291
0
}
292
293
294
NS_IMETHODIMP
295
TableRowsCollection::ParentDestroyed()
296
0
{
297
0
  CleanUp();
298
0
  return NS_OK;
299
0
}
300
301
bool
302
TableRowsCollection::InterestingContainer(nsIContent* aContainer)
303
0
{
304
0
  return mParent && aContainer &&
305
0
    (aContainer == mParent ||
306
0
     (aContainer->GetParent() == mParent &&
307
0
      aContainer->IsAnyOfHTMLElements(nsGkAtoms::thead,
308
0
                                      nsGkAtoms::tbody,
309
0
                                      nsGkAtoms::tfoot)));
310
0
}
311
312
bool
313
TableRowsCollection::IsAppropriateRow(nsAtom* aSection, nsIContent* aContent)
314
0
{
315
0
  if (!aContent->IsHTMLElement(nsGkAtoms::tr)) {
316
0
    return false;
317
0
  }
318
0
  // If it's in the root, then we consider it to be in a tbody.
319
0
  nsIContent* parent = aContent->GetParent();
320
0
  if (aSection == nsGkAtoms::tbody && parent == mParent) {
321
0
    return true;
322
0
  }
323
0
  return parent->IsHTMLElement(aSection);
324
0
}
325
326
nsIContent*
327
TableRowsCollection::PreviousRow(nsAtom* aSection, nsIContent* aCurrent)
328
0
{
329
0
  // Keep going backwards until we've found a `tr` element. We want to always
330
0
  // run at least once, as we don't want to find ourselves.
331
0
  //
332
0
  // Each spin of the loop we step backwards one element. If we're at the top of
333
0
  // a section, we step out of it into the root, and if we step onto a section
334
0
  // matching `aSection`, we step into it. We keep spinning the loop until
335
0
  // either we reach the first element in mParent, or find a <tr> in an
336
0
  // appropriate section.
337
0
  nsIContent* prev = aCurrent;
338
0
  do {
339
0
    nsIContent* parent = prev->GetParent();
340
0
    prev = prev->GetPreviousSibling();
341
0
342
0
    // Ascend out of any sections we're currently in, if we've run out of
343
0
    // elements.
344
0
    if (!prev && parent != mParent) {
345
0
      prev = parent->GetPreviousSibling();
346
0
    }
347
0
348
0
    // Descend into a section if we stepped onto one.
349
0
    if (prev && prev->GetParent() == mParent && prev->IsHTMLElement(aSection)) {
350
0
      prev = prev->GetLastChild();
351
0
    }
352
0
  } while (prev && !IsAppropriateRow(aSection, prev));
353
0
  return prev;
354
0
}
355
356
int32_t
357
TableRowsCollection::HandleInsert(nsIContent* aContainer,
358
                                  nsIContent* aChild,
359
                                  int32_t aIndexGuess)
360
0
{
361
0
  if (!nsContentUtils::IsInSameAnonymousTree(mParent, aChild)) {
362
0
    return aIndexGuess; // Nothing inserted, guess hasn't changed.
363
0
  }
364
0
365
0
  // If we're adding a section to the root, add each of the rows in that section
366
0
  // individually.
367
0
  if (aContainer == mParent &&
368
0
      aChild->IsAnyOfHTMLElements(nsGkAtoms::thead,
369
0
                                  nsGkAtoms::tbody,
370
0
                                  nsGkAtoms::tfoot)) {
371
0
    // If we're entering a tbody, we can persist the index guess we were passed,
372
0
    // as the newly added items are in the same section as us, however, if we're
373
0
    // entering thead or tfoot we will have to re-scan.
374
0
    bool isTBody = aChild->IsHTMLElement(nsGkAtoms::tbody);
375
0
    int32_t indexGuess = isTBody ? aIndexGuess : -1;
376
0
377
0
    for (nsIContent* inner = aChild->GetFirstChild();
378
0
         inner; inner = inner->GetNextSibling()) {
379
0
      indexGuess = HandleInsert(aChild, inner, indexGuess);
380
0
    }
381
0
382
0
    return isTBody ? indexGuess : -1;
383
0
  }
384
0
  if (!aChild->IsHTMLElement(nsGkAtoms::tr)) {
385
0
    return aIndexGuess; // Nothing inserted, guess hasn't changed.
386
0
  }
387
0
388
0
  // We should have only been passed an insertion from an interesting container,
389
0
  // so we can get the container we're inserting to fairly easily.
390
0
  nsAtom* section = aContainer == mParent
391
0
    ? nsGkAtoms::tbody
392
0
    : aContainer->NodeInfo()->NameAtom();
393
0
394
0
  // Determine the default index we would to insert after if we don't find any
395
0
  // previous row, and offset our section boundaries based on the section we're
396
0
  // planning to insert into.
397
0
  size_t index = 0;
398
0
  if (section == nsGkAtoms::thead) {
399
0
    mBodyStart++;
400
0
    mFootStart++;
401
0
  } else if (section == nsGkAtoms::tbody) {
402
0
    index = mBodyStart;
403
0
    mFootStart++;
404
0
  } else if (section == nsGkAtoms::tfoot) {
405
0
    index = mFootStart;
406
0
  } else {
407
0
    MOZ_ASSERT(false, "section should be one of thead, tbody, or tfoot");
408
0
  }
409
0
410
0
  // If we already have an index guess, we can skip scanning for the previous row.
411
0
  if (aIndexGuess >= 0) {
412
0
    index = aIndexGuess;
413
0
  } else {
414
0
    // Find the previous row in the section we're inserting into. If we find it,
415
0
    // we can use it to override our insertion index. We don't need to modify
416
0
    // mBodyStart or mFootStart anymore, as they have already been correctly
417
0
    // updated based only on section.
418
0
    nsIContent* insertAfter = PreviousRow(section, aChild);
419
0
    if (insertAfter) {
420
0
      // NOTE: We want to ensure that appending elements is quick, so we search
421
0
      // from the end rather than from the beginning.
422
0
      index = mRows.LastIndexOf(insertAfter) + 1;
423
0
      MOZ_ASSERT(index != nsTArray<nsCOMPtr<nsIContent>>::NoIndex);
424
0
    }
425
0
  }
426
0
427
#ifdef DEBUG
428
  // Assert that we're inserting into the correct section.
429
  if (section == nsGkAtoms::thead) {
430
    MOZ_ASSERT(index < mBodyStart);
431
  } else if (section == nsGkAtoms::tbody) {
432
    MOZ_ASSERT(index >= mBodyStart);
433
    MOZ_ASSERT(index < mFootStart);
434
  } else if (section == nsGkAtoms::tfoot) {
435
    MOZ_ASSERT(index >= mFootStart);
436
    MOZ_ASSERT(index <= mRows.Length());
437
  }
438
439
  MOZ_ASSERT(mBodyStart <= mFootStart);
440
  MOZ_ASSERT(mFootStart <= mRows.Length() + 1);
441
#endif
442
443
0
  mRows.InsertElementAt(index, aChild);
444
0
  return index + 1;
445
0
}
446
447
// nsIMutationObserver
448
449
void
450
TableRowsCollection::ContentAppended(nsIContent* aFirstNewContent)
451
0
{
452
0
  nsIContent* container = aFirstNewContent->GetParent();
453
0
  if (!nsContentUtils::IsInSameAnonymousTree(mParent, aFirstNewContent) ||
454
0
      !InterestingContainer(container)) {
455
0
    return;
456
0
  }
457
0
458
0
  // We usually can't guess where we need to start inserting, unless we're
459
0
  // appending into mParent, in which case we can provide the guess that we
460
0
  // should insert at the end of the body, which can help us avoid potentially
461
0
  // expensive work in the common case.
462
0
  int32_t indexGuess = mParent == container ? mFootStart : -1;
463
0
464
0
  // Insert each of the newly added content one at a time. The indexGuess should
465
0
  // make insertions of a large number of elements cheaper.
466
0
  for (nsIContent* content = aFirstNewContent;
467
0
       content; content = content->GetNextSibling()) {
468
0
    indexGuess = HandleInsert(container, content, indexGuess);
469
0
  }
470
0
}
471
472
void
473
TableRowsCollection::ContentInserted(nsIContent* aChild)
474
0
{
475
0
  if (!nsContentUtils::IsInSameAnonymousTree(mParent, aChild) ||
476
0
      !InterestingContainer(aChild->GetParent())) {
477
0
    return;
478
0
  }
479
0
480
0
  HandleInsert(aChild->GetParent(), aChild);
481
0
}
482
483
void
484
TableRowsCollection::ContentRemoved(nsIContent* aChild,
485
                                    nsIContent* aPreviousSibling)
486
0
{
487
0
  if (!nsContentUtils::IsInSameAnonymousTree(mParent, aChild) ||
488
0
      !InterestingContainer(aChild->GetParent())) {
489
0
    return;
490
0
  }
491
0
492
0
  // If the element being removed is a `tr`, we can just remove it from our
493
0
  // list. It shouldn't change the order of anything.
494
0
  if (aChild->IsHTMLElement(nsGkAtoms::tr)) {
495
0
    size_t index = mRows.IndexOf(aChild);
496
0
    if (index != nsTArray<nsCOMPtr<nsIContent>>::NoIndex) {
497
0
      mRows.RemoveElementAt(index);
498
0
      if (mBodyStart > index) {
499
0
        mBodyStart--;
500
0
      }
501
0
      if (mFootStart > index) {
502
0
        mFootStart--;
503
0
      }
504
0
    }
505
0
    return;
506
0
  }
507
0
508
0
  // If the element being removed is a `thead`, `tbody`, or `tfoot`, we can
509
0
  // remove any `tr`s in our list which have that element as its parent node. In
510
0
  // any other situation, the removal won't affect us, so we can ignore it.
511
0
  if (!aChild->IsAnyOfHTMLElements(nsGkAtoms::thead, nsGkAtoms::tbody, nsGkAtoms::tfoot)) {
512
0
    return;
513
0
  }
514
0
515
0
  size_t beforeLength = mRows.Length();
516
0
  mRows.RemoveElementsBy([&] (nsIContent* element) {
517
0
    return element->GetParent() == aChild;
518
0
  });
519
0
  size_t removed = beforeLength - mRows.Length();
520
0
  if (aChild->IsHTMLElement(nsGkAtoms::thead)) {
521
0
    // NOTE: Need to move both tbody and tfoot, as we removed from head.
522
0
    mBodyStart -= removed;
523
0
    mFootStart -= removed;
524
0
  } else if (aChild->IsHTMLElement(nsGkAtoms::tbody)) {
525
0
    // NOTE: Need to move tfoot, as we removed from body.
526
0
    mFootStart -= removed;
527
0
  }
528
0
}
529
530
void
531
TableRowsCollection::NodeWillBeDestroyed(const nsINode* aNode)
532
0
{
533
0
  // Set mInitialized to false so CleanUp doesn't try to remove our mutation
534
0
  // observer, as we're going away. CleanUp() will reset mInitialized to true as
535
0
  // it returns.
536
0
  mInitialized = false;
537
0
  CleanUp();
538
0
}
539
540
/* --------------------------- HTMLTableElement ---------------------------- */
541
542
HTMLTableElement::HTMLTableElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
543
  : nsGenericHTMLElement(std::move(aNodeInfo)),
544
    mTableInheritedAttributes(nullptr)
545
0
{
546
0
  SetHasWeirdParserInsertionMode();
547
0
}
548
549
HTMLTableElement::~HTMLTableElement()
550
0
{
551
0
  if (mRows) {
552
0
    mRows->ParentDestroyed();
553
0
  }
554
0
  ReleaseInheritedAttributes();
555
0
}
556
557
JSObject*
558
HTMLTableElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
559
0
{
560
0
  return HTMLTableElement_Binding::Wrap(aCx, this, aGivenProto);
561
0
}
562
563
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLTableElement)
564
565
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLTableElement, nsGenericHTMLElement)
566
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTBodies)
567
0
  if (tmp->mRows) {
568
0
    tmp->mRows->ParentDestroyed();
569
0
  }
570
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRows)
571
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
572
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTableElement,
573
0
                                                  nsGenericHTMLElement)
574
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTBodies)
575
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRows)
576
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
577
578
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLTableElement,
579
                                               nsGenericHTMLElement)
580
581
NS_IMPL_ELEMENT_CLONE(HTMLTableElement)
582
583
584
// the DOM spec says border, cellpadding, cellSpacing are all "wstring"
585
// in fact, they are integers or they are meaningless.  so we store them
586
// here as ints.
587
588
nsIHTMLCollection*
589
HTMLTableElement::Rows()
590
0
{
591
0
  if (!mRows) {
592
0
    mRows = new TableRowsCollection(this);
593
0
  }
594
0
595
0
  return mRows;
596
0
}
597
598
nsIHTMLCollection*
599
HTMLTableElement::TBodies()
600
0
{
601
0
  if (!mTBodies) {
602
0
    // Not using NS_GetContentList because this should not be cached
603
0
    mTBodies = new nsContentList(this,
604
0
                                 kNameSpaceID_XHTML,
605
0
                                 nsGkAtoms::tbody,
606
0
                                 nsGkAtoms::tbody,
607
0
                                 false);
608
0
  }
609
0
610
0
  return mTBodies;
611
0
}
612
613
already_AddRefed<nsGenericHTMLElement>
614
HTMLTableElement::CreateTHead()
615
0
{
616
0
  RefPtr<nsGenericHTMLElement> head = GetTHead();
617
0
  if (!head) {
618
0
    // Create a new head rowgroup.
619
0
    RefPtr<mozilla::dom::NodeInfo> nodeInfo;
620
0
    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::thead,
621
0
                                 getter_AddRefs(nodeInfo));
622
0
623
0
    head = NS_NewHTMLTableSectionElement(nodeInfo.forget());
624
0
    if (!head) {
625
0
      return nullptr;
626
0
    }
627
0
628
0
    nsCOMPtr<nsIContent> refNode = nullptr;
629
0
    for (refNode = nsINode::GetFirstChild();
630
0
         refNode;
631
0
         refNode = refNode->GetNextSibling()) {
632
0
633
0
      if (refNode->IsHTMLElement() &&
634
0
          !refNode->IsHTMLElement(nsGkAtoms::caption) &&
635
0
          !refNode->IsHTMLElement(nsGkAtoms::colgroup)) {
636
0
        break;
637
0
      }
638
0
    }
639
0
640
0
    nsINode::InsertBefore(*head, refNode, IgnoreErrors());
641
0
  }
642
0
  return head.forget();
643
0
}
644
645
void
646
HTMLTableElement::DeleteTHead()
647
0
{
648
0
  HTMLTableSectionElement* tHead = GetTHead();
649
0
  if (tHead) {
650
0
    mozilla::ErrorResult rv;
651
0
    nsINode::RemoveChild(*tHead, rv);
652
0
    MOZ_ASSERT(!rv.Failed());
653
0
  }
654
0
}
655
656
already_AddRefed<nsGenericHTMLElement>
657
HTMLTableElement::CreateTFoot()
658
0
{
659
0
  RefPtr<nsGenericHTMLElement> foot = GetTFoot();
660
0
  if (!foot) {
661
0
    // create a new foot rowgroup
662
0
    RefPtr<mozilla::dom::NodeInfo> nodeInfo;
663
0
    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tfoot,
664
0
                                 getter_AddRefs(nodeInfo));
665
0
666
0
    foot = NS_NewHTMLTableSectionElement(nodeInfo.forget());
667
0
    if (!foot) {
668
0
      return nullptr;
669
0
    }
670
0
    AppendChildTo(foot, true);
671
0
  }
672
0
673
0
  return foot.forget();
674
0
}
675
676
void
677
HTMLTableElement::DeleteTFoot()
678
0
{
679
0
  HTMLTableSectionElement* tFoot = GetTFoot();
680
0
  if (tFoot) {
681
0
    mozilla::ErrorResult rv;
682
0
    nsINode::RemoveChild(*tFoot, rv);
683
0
    MOZ_ASSERT(!rv.Failed());
684
0
  }
685
0
}
686
687
already_AddRefed<nsGenericHTMLElement>
688
HTMLTableElement::CreateCaption()
689
0
{
690
0
  RefPtr<nsGenericHTMLElement> caption = GetCaption();
691
0
  if (!caption) {
692
0
    // Create a new caption.
693
0
    RefPtr<mozilla::dom::NodeInfo> nodeInfo;
694
0
    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::caption,
695
0
                                 getter_AddRefs(nodeInfo));
696
0
697
0
    caption = NS_NewHTMLTableCaptionElement(nodeInfo.forget());
698
0
    if (!caption) {
699
0
      return nullptr;
700
0
    }
701
0
702
0
    nsCOMPtr<nsINode> firsChild = nsINode::GetFirstChild();
703
0
    nsINode::InsertBefore(*caption, firsChild, IgnoreErrors());
704
0
  }
705
0
  return caption.forget();
706
0
}
707
708
void
709
HTMLTableElement::DeleteCaption()
710
0
{
711
0
  HTMLTableCaptionElement* caption = GetCaption();
712
0
  if (caption) {
713
0
    mozilla::ErrorResult rv;
714
0
    nsINode::RemoveChild(*caption, rv);
715
0
    MOZ_ASSERT(!rv.Failed());
716
0
  }
717
0
}
718
719
already_AddRefed<nsGenericHTMLElement>
720
HTMLTableElement::CreateTBody()
721
0
{
722
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo =
723
0
    OwnerDoc()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::tbody, nullptr,
724
0
                                               kNameSpaceID_XHTML,
725
0
                                               ELEMENT_NODE);
726
0
  MOZ_ASSERT(nodeInfo);
727
0
728
0
  RefPtr<nsGenericHTMLElement> newBody =
729
0
    NS_NewHTMLTableSectionElement(nodeInfo.forget());
730
0
  MOZ_ASSERT(newBody);
731
0
732
0
  nsCOMPtr<nsIContent> referenceNode = nullptr;
733
0
  for (nsIContent* child = nsINode::GetLastChild();
734
0
       child;
735
0
       child = child->GetPreviousSibling()) {
736
0
    if (child->IsHTMLElement(nsGkAtoms::tbody)) {
737
0
      referenceNode = child->GetNextSibling();
738
0
      break;
739
0
    }
740
0
  }
741
0
742
0
  nsINode::InsertBefore(*newBody, referenceNode, IgnoreErrors());
743
0
744
0
  return newBody.forget();
745
0
}
746
747
already_AddRefed<nsGenericHTMLElement>
748
HTMLTableElement::InsertRow(int32_t aIndex, ErrorResult& aError)
749
0
{
750
0
  /* get the ref row at aIndex
751
0
     if there is one,
752
0
       get its parent
753
0
       insert the new row just before the ref row
754
0
     else
755
0
       get the first row group
756
0
       insert the new row as its first child
757
0
  */
758
0
  if (aIndex < -1) {
759
0
    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
760
0
    return nullptr;
761
0
  }
762
0
763
0
  nsIHTMLCollection* rows = Rows();
764
0
  uint32_t rowCount = rows->Length();
765
0
  if ((uint32_t)aIndex > rowCount && aIndex != -1) {
766
0
    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
767
0
    return nullptr;
768
0
  }
769
0
770
0
  // use local variable refIndex so we can remember original aIndex
771
0
  uint32_t refIndex = (uint32_t)aIndex;
772
0
773
0
  RefPtr<nsGenericHTMLElement> newRow;
774
0
  if (rowCount > 0) {
775
0
    if (refIndex == rowCount || aIndex == -1) {
776
0
      // we set refIndex to the last row so we can get the last row's
777
0
      // parent we then do an AppendChild below if (rowCount<aIndex)
778
0
779
0
      refIndex = rowCount - 1;
780
0
    }
781
0
782
0
    RefPtr<Element> refRow = rows->Item(refIndex);
783
0
    nsCOMPtr<nsINode> parent = refRow->GetParentNode();
784
0
785
0
    // create the row
786
0
    RefPtr<mozilla::dom::NodeInfo> nodeInfo;
787
0
    nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr,
788
0
                                 getter_AddRefs(nodeInfo));
789
0
790
0
    newRow = NS_NewHTMLTableRowElement(nodeInfo.forget());
791
0
792
0
    if (newRow) {
793
0
      // If aIndex is -1 or equal to the number of rows, the new row
794
0
      // is appended.
795
0
      if (aIndex == -1 || uint32_t(aIndex) == rowCount) {
796
0
        parent->AppendChild(*newRow, aError);
797
0
      } else {
798
0
        // insert the new row before the reference row we found above
799
0
        parent->InsertBefore(*newRow, refRow, aError);
800
0
      }
801
0
802
0
      if (aError.Failed()) {
803
0
        return nullptr;
804
0
      }
805
0
    }
806
0
  } else {
807
0
    // the row count was 0, so
808
0
    // find the last row group and insert there as first child
809
0
    nsCOMPtr<nsIContent> rowGroup;
810
0
    for (nsIContent* child = nsINode::GetLastChild();
811
0
         child;
812
0
         child = child->GetPreviousSibling()) {
813
0
      if (child->IsHTMLElement(nsGkAtoms::tbody)) {
814
0
        rowGroup = child;
815
0
        break;
816
0
      }
817
0
    }
818
0
819
0
    if (!rowGroup) { // need to create a TBODY
820
0
      RefPtr<mozilla::dom::NodeInfo> nodeInfo;
821
0
      nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tbody,
822
0
                                   getter_AddRefs(nodeInfo));
823
0
824
0
      rowGroup = NS_NewHTMLTableSectionElement(nodeInfo.forget());
825
0
      if (rowGroup) {
826
0
        aError = AppendChildTo(rowGroup, true);
827
0
        if (aError.Failed()) {
828
0
          return nullptr;
829
0
        }
830
0
      }
831
0
    }
832
0
833
0
    if (rowGroup) {
834
0
      RefPtr<mozilla::dom::NodeInfo> nodeInfo;
835
0
      nsContentUtils::QNameChanged(mNodeInfo, nsGkAtoms::tr,
836
0
                                   getter_AddRefs(nodeInfo));
837
0
838
0
      newRow = NS_NewHTMLTableRowElement(nodeInfo.forget());
839
0
      if (newRow) {
840
0
        HTMLTableSectionElement* section =
841
0
          static_cast<HTMLTableSectionElement*>(rowGroup.get());
842
0
        nsIHTMLCollection* rows = section->Rows();
843
0
        nsCOMPtr<nsINode> refNode = rows->Item(0);
844
0
        rowGroup->InsertBefore(*newRow, refNode, aError);
845
0
      }
846
0
    }
847
0
  }
848
0
849
0
  return newRow.forget();
850
0
}
851
852
void
853
HTMLTableElement::DeleteRow(int32_t aIndex, ErrorResult& aError)
854
0
{
855
0
  if (aIndex < -1) {
856
0
    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
857
0
    return;
858
0
  }
859
0
860
0
  nsIHTMLCollection* rows = Rows();
861
0
  uint32_t refIndex;
862
0
  if (aIndex == -1) {
863
0
    refIndex = rows->Length();
864
0
    if (refIndex == 0) {
865
0
      return;
866
0
    }
867
0
868
0
    --refIndex;
869
0
  } else {
870
0
    refIndex = (uint32_t)aIndex;
871
0
  }
872
0
873
0
  nsCOMPtr<nsIContent> row = rows->Item(refIndex);
874
0
  if (!row) {
875
0
    aError.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
876
0
    return;
877
0
  }
878
0
879
0
  row->RemoveFromParent();
880
0
}
881
882
bool
883
HTMLTableElement::ParseAttribute(int32_t aNamespaceID,
884
                                 nsAtom* aAttribute,
885
                                 const nsAString& aValue,
886
                                 nsIPrincipal* aMaybeScriptedPrincipal,
887
                                 nsAttrValue& aResult)
888
0
{
889
0
  /* ignore summary, just a string */
890
0
  if (aNamespaceID == kNameSpaceID_None) {
891
0
    if (aAttribute == nsGkAtoms::cellspacing ||
892
0
        aAttribute == nsGkAtoms::cellpadding ||
893
0
        aAttribute == nsGkAtoms::border) {
894
0
      return aResult.ParseNonNegativeIntValue(aValue);
895
0
    }
896
0
    if (aAttribute == nsGkAtoms::height) {
897
0
      return aResult.ParseSpecialIntValue(aValue);
898
0
    }
899
0
    if (aAttribute == nsGkAtoms::width) {
900
0
      if (aResult.ParseSpecialIntValue(aValue)) {
901
0
        // treat 0 width as auto
902
0
        nsAttrValue::ValueType type = aResult.Type();
903
0
        return !((type == nsAttrValue::eInteger &&
904
0
                  aResult.GetIntegerValue() == 0) ||
905
0
                 (type == nsAttrValue::ePercent &&
906
0
                  aResult.GetPercentValue() == 0.0f));
907
0
      }
908
0
      return false;
909
0
    }
910
0
911
0
    if (aAttribute == nsGkAtoms::align) {
912
0
      return ParseTableHAlignValue(aValue, aResult);
913
0
    }
914
0
    if (aAttribute == nsGkAtoms::bgcolor ||
915
0
        aAttribute == nsGkAtoms::bordercolor) {
916
0
      return aResult.ParseColor(aValue);
917
0
    }
918
0
    if (aAttribute == nsGkAtoms::hspace ||
919
0
        aAttribute == nsGkAtoms::vspace) {
920
0
      return aResult.ParseIntWithBounds(aValue, 0);
921
0
    }
922
0
  }
923
0
924
0
  return nsGenericHTMLElement::ParseBackgroundAttribute(aNamespaceID,
925
0
                                                        aAttribute, aValue,
926
0
                                                        aResult) ||
927
0
         nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
928
0
                                              aMaybeScriptedPrincipal, aResult);
929
0
}
930
931
932
933
void
934
HTMLTableElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
935
                                        MappedDeclarations& aDecls)
936
0
{
937
0
  // XXX Bug 211636:  This function is used by a single style rule
938
0
  // that's used to match two different type of elements -- tables, and
939
0
  // table cells.  (nsHTMLTableCellElement overrides
940
0
  // WalkContentStyleRules so that this happens.)  This violates the
941
0
  // nsIStyleRule contract, since it's the same style rule object doing
942
0
  // the mapping in two different ways.  It's also incorrect since it's
943
0
  // testing the display type of the ComputedStyle rather than checking
944
0
  // which *element* it's matching (style rules should not stop matching
945
0
  // when the display type is changed).
946
0
947
0
  nsCompatibility mode = aDecls.Document()->GetCompatibilityMode();
948
0
949
0
  // cellspacing
950
0
  const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellspacing);
951
0
  if (value && value->Type() == nsAttrValue::eInteger &&
952
0
      !aDecls.PropertyIsSet(eCSSProperty_border_spacing)) {
953
0
    aDecls.SetPixelValue(eCSSProperty_border_spacing, float(value->GetIntegerValue()));
954
0
  }
955
0
  // align; Check for enumerated type (it may be another type if
956
0
  // illegal)
957
0
  value = aAttributes->GetAttr(nsGkAtoms::align);
958
0
  if (value && value->Type() == nsAttrValue::eEnum) {
959
0
    if (value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_CENTER ||
960
0
        value->GetEnumValue() == NS_STYLE_TEXT_ALIGN_MOZ_CENTER) {
961
0
      aDecls.SetAutoValueIfUnset(eCSSProperty_margin_left);
962
0
      aDecls.SetAutoValueIfUnset(eCSSProperty_margin_right);
963
0
    }
964
0
  }
965
0
966
0
  // hspace is mapped into left and right margin,
967
0
  // vspace is mapped into top and bottom margins
968
0
  // - *** Quirks Mode only ***
969
0
  if (eCompatibility_NavQuirks == mode) {
970
0
    value = aAttributes->GetAttr(nsGkAtoms::hspace);
971
0
972
0
    if (value && value->Type() == nsAttrValue::eInteger) {
973
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left, (float)value->GetIntegerValue());
974
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right, (float)value->GetIntegerValue());
975
0
    }
976
0
977
0
    value = aAttributes->GetAttr(nsGkAtoms::vspace);
978
0
979
0
    if (value && value->Type() == nsAttrValue::eInteger) {
980
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top, (float)value->GetIntegerValue());
981
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom, (float)value->GetIntegerValue());
982
0
    }
983
0
  }
984
0
  // bordercolor
985
0
  value = aAttributes->GetAttr(nsGkAtoms::bordercolor);
986
0
  nscolor color;
987
0
  if (value && value->GetColorValue(color)) {
988
0
    aDecls.SetColorValueIfUnset(eCSSProperty_border_top_color, color);
989
0
    aDecls.SetColorValueIfUnset(eCSSProperty_border_left_color, color);
990
0
    aDecls.SetColorValueIfUnset(eCSSProperty_border_bottom_color, color);
991
0
    aDecls.SetColorValueIfUnset(eCSSProperty_border_right_color, color);
992
0
  }
993
0
994
0
  // border
995
0
  const nsAttrValue* borderValue = aAttributes->GetAttr(nsGkAtoms::border);
996
0
  if (borderValue) {
997
0
    // border = 1 pixel default
998
0
    int32_t borderThickness = 1;
999
0
1000
0
    if (borderValue->Type() == nsAttrValue::eInteger)
1001
0
      borderThickness = borderValue->GetIntegerValue();
1002
0
1003
0
    // by default, set all border sides to the specified width
1004
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_border_top_width, (float)borderThickness);
1005
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_border_left_width, (float)borderThickness);
1006
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_border_bottom_width, (float)borderThickness);
1007
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_border_right_width, (float)borderThickness);
1008
0
  }
1009
0
1010
0
  nsGenericHTMLElement::MapImageSizeAttributesInto(aAttributes, aDecls);
1011
0
  nsGenericHTMLElement::MapBackgroundAttributesInto(aAttributes, aDecls);
1012
0
  nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls);
1013
0
}
1014
1015
NS_IMETHODIMP_(bool)
1016
HTMLTableElement::IsAttributeMapped(const nsAtom* aAttribute) const
1017
0
{
1018
0
  static const MappedAttributeEntry attributes[] = {
1019
0
    { &nsGkAtoms::cellpadding },
1020
0
    { &nsGkAtoms::cellspacing },
1021
0
    { &nsGkAtoms::border },
1022
0
    { &nsGkAtoms::width },
1023
0
    { &nsGkAtoms::height },
1024
0
    { &nsGkAtoms::hspace },
1025
0
    { &nsGkAtoms::vspace },
1026
0
1027
0
    { &nsGkAtoms::bordercolor },
1028
0
1029
0
    { &nsGkAtoms::align },
1030
0
    { nullptr }
1031
0
  };
1032
0
1033
0
  static const MappedAttributeEntry* const map[] = {
1034
0
    attributes,
1035
0
    sCommonAttributeMap,
1036
0
    sBackgroundAttributeMap,
1037
0
  };
1038
0
1039
0
  return FindAttributeDependence(aAttribute, map);
1040
0
}
1041
1042
nsMapRuleToAttributesFunc
1043
HTMLTableElement::GetAttributeMappingFunction() const
1044
0
{
1045
0
  return &MapAttributesIntoRule;
1046
0
}
1047
1048
static void
1049
MapInheritedTableAttributesIntoRule(const nsMappedAttributes* aAttributes,
1050
                                    MappedDeclarations& aDecls)
1051
0
{
1052
0
  const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::cellpadding);
1053
0
  if (value && value->Type() == nsAttrValue::eInteger) {
1054
0
    // We have cellpadding.  This will override our padding values if we
1055
0
    // don't have any set.
1056
0
    float pad = float(value->GetIntegerValue());
1057
0
1058
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_padding_top, pad);
1059
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_padding_right, pad);
1060
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_padding_bottom, pad);
1061
0
    aDecls.SetPixelValueIfUnset(eCSSProperty_padding_left, pad);
1062
0
  }
1063
0
}
1064
1065
nsMappedAttributes*
1066
HTMLTableElement::GetAttributesMappedForCell()
1067
0
{
1068
0
  return mTableInheritedAttributes;
1069
0
}
1070
1071
void
1072
HTMLTableElement::BuildInheritedAttributes()
1073
0
{
1074
0
  NS_ASSERTION(!mTableInheritedAttributes,
1075
0
               "potential leak, plus waste of work");
1076
0
  MOZ_ASSERT(NS_IsMainThread());
1077
0
  nsIDocument *document = GetComposedDoc();
1078
0
  nsHTMLStyleSheet* sheet = document ?
1079
0
                              document->GetAttributeStyleSheet() : nullptr;
1080
0
  RefPtr<nsMappedAttributes> newAttrs;
1081
0
  if (sheet) {
1082
0
    const nsAttrValue* value = mAttrs.GetAttr(nsGkAtoms::cellpadding);
1083
0
    if (value) {
1084
0
      RefPtr<nsMappedAttributes> modifiableMapped = new
1085
0
      nsMappedAttributes(sheet, MapInheritedTableAttributesIntoRule);
1086
0
1087
0
      if (modifiableMapped) {
1088
0
        nsAttrValue val(*value);
1089
0
        bool oldValueSet;
1090
0
        modifiableMapped->SetAndSwapAttr(nsGkAtoms::cellpadding, val,
1091
0
                                         &oldValueSet);
1092
0
      }
1093
0
      newAttrs = sheet->UniqueMappedAttributes(modifiableMapped);
1094
0
      NS_ASSERTION(newAttrs, "out of memory, but handling gracefully");
1095
0
1096
0
      if (newAttrs != modifiableMapped) {
1097
0
        // Reset the stylesheet of modifiableMapped so that it doesn't
1098
0
        // spend time trying to remove itself from the hash.  There is no
1099
0
        // risk that modifiableMapped is in the hash since we created
1100
0
        // it ourselves and it didn't come from the stylesheet (in which
1101
0
        // case it would not have been modifiable).
1102
0
        modifiableMapped->DropStyleSheetReference();
1103
0
      }
1104
0
    }
1105
0
    mTableInheritedAttributes = newAttrs;
1106
0
    NS_IF_ADDREF(mTableInheritedAttributes);
1107
0
  }
1108
0
}
1109
1110
void
1111
HTMLTableElement::ReleaseInheritedAttributes()
1112
0
{
1113
0
  NS_IF_RELEASE(mTableInheritedAttributes);
1114
0
}
1115
1116
nsresult
1117
HTMLTableElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
1118
                             nsIContent* aBindingParent)
1119
0
{
1120
0
  ReleaseInheritedAttributes();
1121
0
  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
1122
0
                                                aBindingParent);
1123
0
  NS_ENSURE_SUCCESS(rv, rv);
1124
0
  BuildInheritedAttributes();
1125
0
  return NS_OK;
1126
0
}
1127
1128
void
1129
HTMLTableElement::UnbindFromTree(bool aDeep, bool aNullParent)
1130
0
{
1131
0
  ReleaseInheritedAttributes();
1132
0
  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
1133
0
}
1134
1135
nsresult
1136
HTMLTableElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
1137
                                const nsAttrValueOrString* aValue,
1138
                                bool aNotify)
1139
0
{
1140
0
  if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) {
1141
0
    ReleaseInheritedAttributes();
1142
0
  }
1143
0
  return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
1144
0
                                             aNotify);
1145
0
}
1146
1147
nsresult
1148
HTMLTableElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
1149
                               const nsAttrValue* aValue,
1150
                               const nsAttrValue* aOldValue,
1151
                               nsIPrincipal* aSubjectPrincipal,
1152
                               bool aNotify)
1153
0
{
1154
0
  if (aName == nsGkAtoms::cellpadding && aNameSpaceID == kNameSpaceID_None) {
1155
0
    BuildInheritedAttributes();
1156
0
  }
1157
0
  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
1158
0
                                            aOldValue, aSubjectPrincipal, aNotify);
1159
0
}
1160
1161
} // namespace dom
1162
} // namespace mozilla