Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / html / HTMLTableElement.cpp
1 /*
2  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3  *           (C) 1997 Torben Weis (weis@kde.org)
4  *           (C) 1998 Waldo Bastian (bastian@kde.org)
5  *           (C) 1999 Lars Knoll (knoll@kde.org)
6  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
7  * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2010, 2011 Apple Inc. All rights reserved.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24
25 #include "config.h"
26 #include "HTMLTableElement.h"
27
28 #include "CSSImageValue.h"
29 #include "CSSPropertyNames.h"
30 #include "CSSValueKeywords.h"
31 #include "CSSValuePool.h"
32 #include "ExceptionCode.h"
33 #include "ExceptionCodePlaceholder.h"
34 #include "GenericCachedHTMLCollection.h"
35 #include "HTMLNames.h"
36 #include "HTMLParserIdioms.h"
37 #include "HTMLTableCaptionElement.h"
38 #include "HTMLTableRowElement.h"
39 #include "HTMLTableRowsCollection.h"
40 #include "HTMLTableSectionElement.h"
41 #include "NodeRareData.h"
42 #include "RenderTable.h"
43 #include "StyleProperties.h"
44 #include <wtf/Ref.h>
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49
50 HTMLTableElement::HTMLTableElement(const QualifiedName& tagName, Document& document)
51     : HTMLElement(tagName, document)
52     , m_borderAttr(false)
53     , m_borderColorAttr(false)
54     , m_frameAttr(false)
55     , m_rulesAttr(UnsetRules)
56     , m_padding(1)
57 {
58     ASSERT(hasTagName(tableTag));
59 }
60
61 Ref<HTMLTableElement> HTMLTableElement::create(Document& document)
62 {
63     return adoptRef(*new HTMLTableElement(tableTag, document));
64 }
65
66 Ref<HTMLTableElement> HTMLTableElement::create(const QualifiedName& tagName, Document& document)
67 {
68     return adoptRef(*new HTMLTableElement(tagName, document));
69 }
70
71 HTMLTableCaptionElement* HTMLTableElement::caption() const
72 {
73     for (Node* child = firstChild(); child; child = child->nextSibling()) {
74         if (is<HTMLTableCaptionElement>(*child))
75             return downcast<HTMLTableCaptionElement>(child);
76     }
77     return nullptr;
78 }
79
80 void HTMLTableElement::setCaption(PassRefPtr<HTMLTableCaptionElement> newCaption, ExceptionCode& ec)
81 {
82     deleteCaption();
83     if (newCaption)
84         insertBefore(*newCaption, firstChild(), ec);
85 }
86
87 HTMLTableSectionElement* HTMLTableElement::tHead() const
88 {
89     for (Node* child = firstChild(); child; child = child->nextSibling()) {
90         if (child->hasTagName(theadTag))
91             return downcast<HTMLTableSectionElement>(child);
92     }
93     return nullptr;
94 }
95
96 void HTMLTableElement::setTHead(PassRefPtr<HTMLTableSectionElement> newHead, ExceptionCode& ec)
97 {
98     if (UNLIKELY(newHead && !newHead->hasTagName(theadTag))) {
99         ec = HIERARCHY_REQUEST_ERR;
100         return;
101     }
102
103     deleteTHead();
104
105     if (!newHead)
106         return;
107
108     Node* child;
109     for (child = firstChild(); child; child = child->nextSibling())
110         if (child->isElementNode() && !child->hasTagName(captionTag) && !child->hasTagName(colgroupTag))
111             break;
112
113     insertBefore(*newHead, child, ec);
114 }
115
116 HTMLTableSectionElement* HTMLTableElement::tFoot() const
117 {
118     for (Node* child = firstChild(); child; child = child->nextSibling()) {
119         if (child->hasTagName(tfootTag))
120             return downcast<HTMLTableSectionElement>(child);
121     }
122     return nullptr;
123 }
124
125 void HTMLTableElement::setTFoot(PassRefPtr<HTMLTableSectionElement> newFoot, ExceptionCode& ec)
126 {
127     if (UNLIKELY(newFoot && !newFoot->hasTagName(tfootTag))) {
128         ec = HIERARCHY_REQUEST_ERR;
129         return;
130     }
131
132     deleteTFoot();
133
134     if (!newFoot)
135         return;
136
137     Node* child;
138     for (child = firstChild(); child; child = child->nextSibling())
139         if (child->isElementNode() && !child->hasTagName(captionTag) && !child->hasTagName(colgroupTag) && !child->hasTagName(theadTag))
140             break;
141
142     insertBefore(*newFoot, child, ec);
143 }
144
145 Ref<HTMLTableSectionElement> HTMLTableElement::createTHead()
146 {
147     if (HTMLTableSectionElement* existingHead = tHead())
148         return *existingHead;
149     Ref<HTMLTableSectionElement> head = HTMLTableSectionElement::create(theadTag, document());
150     setTHead(head.ptr(), IGNORE_EXCEPTION);
151     return head;
152 }
153
154 void HTMLTableElement::deleteTHead()
155 {
156     if (auto* tHead = this->tHead())
157         removeChild(*tHead, IGNORE_EXCEPTION);
158 }
159
160 Ref<HTMLTableSectionElement> HTMLTableElement::createTFoot()
161 {
162     if (HTMLTableSectionElement* existingFoot = tFoot())
163         return *existingFoot;
164     Ref<HTMLTableSectionElement> foot = HTMLTableSectionElement::create(tfootTag, document());
165     setTFoot(foot.ptr(), IGNORE_EXCEPTION);
166     return foot;
167 }
168
169 void HTMLTableElement::deleteTFoot()
170 {
171     if (auto* tFoot = this->tFoot())
172         removeChild(*tFoot, IGNORE_EXCEPTION);
173 }
174
175 Ref<HTMLTableSectionElement> HTMLTableElement::createTBody()
176 {
177     Ref<HTMLTableSectionElement> body = HTMLTableSectionElement::create(tbodyTag, document());
178     Node* referenceElement = lastBody() ? lastBody()->nextSibling() : nullptr;
179     insertBefore(body.copyRef(), referenceElement, ASSERT_NO_EXCEPTION);
180     return body;
181 }
182
183 Ref<HTMLTableCaptionElement> HTMLTableElement::createCaption()
184 {
185     if (HTMLTableCaptionElement* existingCaption = caption())
186         return *existingCaption;
187     Ref<HTMLTableCaptionElement> caption = HTMLTableCaptionElement::create(captionTag, document());
188     setCaption(caption.ptr(), IGNORE_EXCEPTION);
189     return caption;
190 }
191
192 void HTMLTableElement::deleteCaption()
193 {
194     if (auto* caption = this->caption())
195         removeChild(*caption, IGNORE_EXCEPTION);
196 }
197
198 HTMLTableSectionElement* HTMLTableElement::lastBody() const
199 {
200     for (Node* child = lastChild(); child; child = child->previousSibling()) {
201         if (child->hasTagName(tbodyTag))
202             return downcast<HTMLTableSectionElement>(child);
203     }
204     return nullptr;
205 }
206
207 RefPtr<HTMLElement> HTMLTableElement::insertRow(int index, ExceptionCode& ec)
208 {
209     if (index < -1) {
210         ec = INDEX_SIZE_ERR;
211         return 0;
212     }
213
214     Ref<HTMLTableElement> protectFromMutationEvents(*this);
215
216     RefPtr<HTMLTableRowElement> lastRow = 0;
217     RefPtr<HTMLTableRowElement> row = 0;
218     if (index == -1)
219         lastRow = HTMLTableRowsCollection::lastRow(*this);
220     else {
221         for (int i = 0; i <= index; ++i) {
222             row = HTMLTableRowsCollection::rowAfter(*this, lastRow.get());
223             if (!row) {
224                 if (i != index) {
225                     ec = INDEX_SIZE_ERR;
226                     return 0;
227                 }
228                 break;
229             }
230             lastRow = row;
231         }
232     }
233
234     RefPtr<ContainerNode> parent;
235     if (lastRow)
236         parent = row ? row->parentNode() : lastRow->parentNode();
237     else {
238         parent = lastBody();
239         if (!parent) {
240             Ref<HTMLTableSectionElement> newBody = HTMLTableSectionElement::create(tbodyTag, document());
241             Ref<HTMLTableRowElement> newRow = HTMLTableRowElement::create(document());
242             newBody->appendChild(newRow.copyRef(), ec);
243             appendChild(WTFMove(newBody), ec);
244             return WTFMove(newRow);
245         }
246     }
247
248     Ref<HTMLTableRowElement> newRow = HTMLTableRowElement::create(document());
249     parent->insertBefore(newRow.copyRef(), row.get(), ec);
250     return WTFMove(newRow);
251 }
252
253 void HTMLTableElement::deleteRow(int index, ExceptionCode& ec)
254 {
255     HTMLTableRowElement* row = nullptr;
256     if (index == -1)
257         row = HTMLTableRowsCollection::lastRow(*this);
258     else {
259         for (int i = 0; i <= index; ++i) {
260             row = HTMLTableRowsCollection::rowAfter(*this, row);
261             if (!row)
262                 break;
263         }
264     }
265     if (!row) {
266         ec = INDEX_SIZE_ERR;
267         return;
268     }
269     row->remove(ec);
270 }
271
272 static inline bool isTableCellAncestor(Node* n)
273 {
274     return n->hasTagName(theadTag) || n->hasTagName(tbodyTag) ||
275            n->hasTagName(tfootTag) || n->hasTagName(trTag) ||
276            n->hasTagName(thTag);
277 }
278
279 static bool setTableCellsChanged(Node* n)
280 {
281     ASSERT(n);
282     bool cellChanged = false;
283
284     if (n->hasTagName(tdTag))
285         cellChanged = true;
286     else if (isTableCellAncestor(n)) {
287         for (Node* child = n->firstChild(); child; child = child->nextSibling())
288             cellChanged |= setTableCellsChanged(child);
289     }
290
291     if (cellChanged)
292        n->setNeedsStyleRecalc();
293
294     return cellChanged;
295 }
296
297 static bool getBordersFromFrameAttributeValue(const AtomicString& value, bool& borderTop, bool& borderRight, bool& borderBottom, bool& borderLeft)
298 {
299     borderTop = false;
300     borderRight = false;
301     borderBottom = false;
302     borderLeft = false;
303
304     if (equalIgnoringCase(value, "above"))
305         borderTop = true;
306     else if (equalIgnoringCase(value, "below"))
307         borderBottom = true;
308     else if (equalIgnoringCase(value, "hsides"))
309         borderTop = borderBottom = true;
310     else if (equalIgnoringCase(value, "vsides"))
311         borderLeft = borderRight = true;
312     else if (equalIgnoringCase(value, "lhs"))
313         borderLeft = true;
314     else if (equalIgnoringCase(value, "rhs"))
315         borderRight = true;
316     else if (equalIgnoringCase(value, "box") || equalIgnoringCase(value, "border"))
317         borderTop = borderBottom = borderLeft = borderRight = true;
318     else if (!equalIgnoringCase(value, "void"))
319         return false;
320     return true;
321 }
322
323 void HTMLTableElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
324 {
325     if (name == widthAttr)
326         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
327     else if (name == heightAttr)
328         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
329     else if (name == borderAttr) 
330         addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderWidth, parseBorderWidthAttribute(value), CSSPrimitiveValue::CSS_PX);
331     else if (name == bordercolorAttr) {
332         if (!value.isEmpty())
333             addHTMLColorToStyle(style, CSSPropertyBorderColor, value);
334     } else if (name == bgcolorAttr)
335         addHTMLColorToStyle(style, CSSPropertyBackgroundColor, value);
336     else if (name == backgroundAttr) {
337         String url = stripLeadingAndTrailingHTMLSpaces(value);
338         if (!url.isEmpty())
339             style.setProperty(CSSProperty(CSSPropertyBackgroundImage, CSSImageValue::create(document().completeURL(url).string())));
340     } else if (name == valignAttr) {
341         if (!value.isEmpty())
342             addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, value);
343     } else if (name == cellspacingAttr) {
344         if (!value.isEmpty())
345             addHTMLLengthToStyle(style, CSSPropertyBorderSpacing, value);
346     } else if (name == vspaceAttr) {
347         addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
348         addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
349     } else if (name == hspaceAttr) {
350         addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
351         addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
352     } else if (name == alignAttr) {
353         if (!value.isEmpty()) {
354             if (equalIgnoringCase(value, "center")) {
355                 addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitMarginStart, CSSValueAuto);
356                 addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitMarginEnd, CSSValueAuto);
357             } else
358                 addPropertyToPresentationAttributeStyle(style, CSSPropertyFloat, value);
359         }
360     } else if (name == rulesAttr) {
361         // The presence of a valid rules attribute causes border collapsing to be enabled.
362         if (m_rulesAttr != UnsetRules)
363             addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderCollapse, CSSValueCollapse);
364     } else if (name == frameAttr) {
365         bool borderTop;
366         bool borderRight;
367         bool borderBottom;
368         bool borderLeft;
369         if (getBordersFromFrameAttributeValue(value, borderTop, borderRight, borderBottom, borderLeft)) {
370             addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderWidth, CSSValueThin);
371             addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderTopStyle, borderTop ? CSSValueSolid : CSSValueHidden);
372             addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderBottomStyle, borderBottom ? CSSValueSolid : CSSValueHidden);
373             addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderLeftStyle, borderLeft ? CSSValueSolid : CSSValueHidden);
374             addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderRightStyle, borderRight ? CSSValueSolid : CSSValueHidden);
375         }
376     } else
377         HTMLElement::collectStyleForPresentationAttribute(name, value, style);
378 }
379
380 bool HTMLTableElement::isPresentationAttribute(const QualifiedName& name) const
381 {
382     if (name == widthAttr || name == heightAttr || name == bgcolorAttr || name == backgroundAttr || name == valignAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr || name == cellspacingAttr || name == borderAttr || name == bordercolorAttr || name == frameAttr || name == rulesAttr)
383         return true;
384     return HTMLElement::isPresentationAttribute(name);
385 }
386
387 void HTMLTableElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
388 {
389     CellBorders bordersBefore = cellBorders();
390     unsigned short oldPadding = m_padding;
391
392     if (name == borderAttr)  {
393         // FIXME: This attribute is a mess.
394         m_borderAttr = parseBorderWidthAttribute(value);
395     } else if (name == bordercolorAttr) {
396         m_borderColorAttr = !value.isEmpty();
397     } else if (name == frameAttr) {
398         // FIXME: This attribute is a mess.
399         bool borderTop;
400         bool borderRight;
401         bool borderBottom;
402         bool borderLeft;
403         m_frameAttr = getBordersFromFrameAttributeValue(value, borderTop, borderRight, borderBottom, borderLeft);
404     } else if (name == rulesAttr) {
405         m_rulesAttr = UnsetRules;
406         if (equalIgnoringCase(value, "none"))
407             m_rulesAttr = NoneRules;
408         else if (equalIgnoringCase(value, "groups"))
409             m_rulesAttr = GroupsRules;
410         else if (equalIgnoringCase(value, "rows"))
411             m_rulesAttr = RowsRules;
412         else if (equalIgnoringCase(value, "cols"))
413             m_rulesAttr = ColsRules;
414         else if (equalIgnoringCase(value, "all"))
415             m_rulesAttr = AllRules;
416     } else if (name == cellpaddingAttr) {
417         if (!value.isEmpty())
418             m_padding = std::max(0, value.toInt());
419         else
420             m_padding = 1;
421     } else if (name == colsAttr) {
422         // ###
423     } else
424         HTMLElement::parseAttribute(name, value);
425
426     if (bordersBefore != cellBorders() || oldPadding != m_padding) {
427         m_sharedCellStyle = nullptr;
428         bool cellChanged = false;
429         for (Node* child = firstChild(); child; child = child->nextSibling())
430             cellChanged |= setTableCellsChanged(child);
431         if (cellChanged)
432             setNeedsStyleRecalc();
433     }
434 }
435
436 static StyleProperties* leakBorderStyle(CSSValueID value)
437 {
438     RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
439     style->setProperty(CSSPropertyBorderTopStyle, value);
440     style->setProperty(CSSPropertyBorderBottomStyle, value);
441     style->setProperty(CSSPropertyBorderLeftStyle, value);
442     style->setProperty(CSSPropertyBorderRightStyle, value);
443     return style.release().leakRef();
444 }
445
446 const StyleProperties* HTMLTableElement::additionalPresentationAttributeStyle()
447 {
448     if (m_frameAttr)
449         return 0;
450     
451     if (!m_borderAttr && !m_borderColorAttr) {
452         // Setting the border to 'hidden' allows it to win over any border
453         // set on the table's cells during border-conflict resolution.
454         if (m_rulesAttr != UnsetRules) {
455             static StyleProperties* solidBorderStyle = leakBorderStyle(CSSValueHidden);
456             return solidBorderStyle;
457         }
458         return 0;
459     }
460
461     if (m_borderColorAttr) {
462         static StyleProperties* solidBorderStyle = leakBorderStyle(CSSValueSolid);
463         return solidBorderStyle;
464     }
465     static StyleProperties* outsetBorderStyle = leakBorderStyle(CSSValueOutset);
466     return outsetBorderStyle;
467 }
468
469 HTMLTableElement::CellBorders HTMLTableElement::cellBorders() const
470 {
471     switch (m_rulesAttr) {
472         case NoneRules:
473         case GroupsRules:
474             return NoBorders;
475         case AllRules:
476             return SolidBorders;
477         case ColsRules:
478             return SolidBordersColsOnly;
479         case RowsRules:
480             return SolidBordersRowsOnly;
481         case UnsetRules:
482             if (!m_borderAttr)
483                 return NoBorders;
484             if (m_borderColorAttr)
485                 return SolidBorders;
486             return InsetBorders;
487     }
488     ASSERT_NOT_REACHED();
489     return NoBorders;
490 }
491
492 RefPtr<StyleProperties> HTMLTableElement::createSharedCellStyle()
493 {
494     RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
495
496     auto& cssValuePool = CSSValuePool::singleton();
497     switch (cellBorders()) {
498     case SolidBordersColsOnly:
499         style->setProperty(CSSPropertyBorderLeftWidth, CSSValueThin);
500         style->setProperty(CSSPropertyBorderRightWidth, CSSValueThin);
501         style->setProperty(CSSPropertyBorderLeftStyle, CSSValueSolid);
502         style->setProperty(CSSPropertyBorderRightStyle, CSSValueSolid);
503         style->setProperty(CSSPropertyBorderColor, cssValuePool.createInheritedValue());
504         break;
505     case SolidBordersRowsOnly:
506         style->setProperty(CSSPropertyBorderTopWidth, CSSValueThin);
507         style->setProperty(CSSPropertyBorderBottomWidth, CSSValueThin);
508         style->setProperty(CSSPropertyBorderTopStyle, CSSValueSolid);
509         style->setProperty(CSSPropertyBorderBottomStyle, CSSValueSolid);
510         style->setProperty(CSSPropertyBorderColor, cssValuePool.createInheritedValue());
511         break;
512     case SolidBorders:
513         style->setProperty(CSSPropertyBorderWidth, cssValuePool.createValue(1, CSSPrimitiveValue::CSS_PX));
514         style->setProperty(CSSPropertyBorderStyle, cssValuePool.createIdentifierValue(CSSValueSolid));
515         style->setProperty(CSSPropertyBorderColor, cssValuePool.createInheritedValue());
516         break;
517     case InsetBorders:
518         style->setProperty(CSSPropertyBorderWidth, cssValuePool.createValue(1, CSSPrimitiveValue::CSS_PX));
519         style->setProperty(CSSPropertyBorderStyle, cssValuePool.createIdentifierValue(CSSValueInset));
520         style->setProperty(CSSPropertyBorderColor, cssValuePool.createInheritedValue());
521         break;
522     case NoBorders:
523         // If 'rules=none' then allow any borders set at cell level to take effect. 
524         break;
525     }
526
527     if (m_padding)
528         style->setProperty(CSSPropertyPadding, cssValuePool.createValue(m_padding, CSSPrimitiveValue::CSS_PX));
529
530     return style;
531 }
532
533 const StyleProperties* HTMLTableElement::additionalCellStyle()
534 {
535     if (!m_sharedCellStyle)
536         m_sharedCellStyle = createSharedCellStyle();
537     return m_sharedCellStyle.get();
538 }
539
540 static StyleProperties* leakGroupBorderStyle(int rows)
541 {
542     RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
543     if (rows) {
544         style->setProperty(CSSPropertyBorderTopWidth, CSSValueThin);
545         style->setProperty(CSSPropertyBorderBottomWidth, CSSValueThin);
546         style->setProperty(CSSPropertyBorderTopStyle, CSSValueSolid);
547         style->setProperty(CSSPropertyBorderBottomStyle, CSSValueSolid);
548     } else {
549         style->setProperty(CSSPropertyBorderLeftWidth, CSSValueThin);
550         style->setProperty(CSSPropertyBorderRightWidth, CSSValueThin);
551         style->setProperty(CSSPropertyBorderLeftStyle, CSSValueSolid);
552         style->setProperty(CSSPropertyBorderRightStyle, CSSValueSolid);
553     }
554     return style.release().leakRef();
555 }
556
557 const StyleProperties* HTMLTableElement::additionalGroupStyle(bool rows)
558 {
559     if (m_rulesAttr != GroupsRules)
560         return 0;
561
562     if (rows) {
563         static StyleProperties* rowBorderStyle = leakGroupBorderStyle(true);
564         return rowBorderStyle;
565     }
566     static StyleProperties* columnBorderStyle = leakGroupBorderStyle(false);
567     return columnBorderStyle;
568 }
569
570 bool HTMLTableElement::isURLAttribute(const Attribute& attribute) const
571 {
572     return attribute.name() == backgroundAttr || HTMLElement::isURLAttribute(attribute);
573 }
574
575 Ref<HTMLCollection> HTMLTableElement::rows()
576 {
577     return ensureRareData().ensureNodeLists().addCachedCollection<HTMLTableRowsCollection>(*this, TableRows);
578 }
579
580 Ref<HTMLCollection> HTMLTableElement::tBodies()
581 {
582     return ensureRareData().ensureNodeLists().addCachedCollection<GenericCachedHTMLCollection<CollectionTypeTraits<TableTBodies>::traversalType>>(*this, TableTBodies);
583 }
584
585 const AtomicString& HTMLTableElement::rules() const
586 {
587     return fastGetAttribute(rulesAttr);
588 }
589
590 const AtomicString& HTMLTableElement::summary() const
591 {
592     return fastGetAttribute(summaryAttr);
593 }
594
595 void HTMLTableElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
596 {
597     HTMLElement::addSubresourceAttributeURLs(urls);
598
599     addSubresourceURL(urls, document().completeURL(fastGetAttribute(backgroundAttr)));
600 }
601
602 }