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