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.
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.
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.
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.
26 #include "core/html/HTMLTableElement.h"
28 #include "bindings/core/v8/ExceptionState.h"
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "core/CSSPropertyNames.h"
31 #include "core/CSSValueKeywords.h"
32 #include "core/HTMLNames.h"
33 #include "core/css/CSSImageValue.h"
34 #include "core/css/CSSValuePool.h"
35 #include "core/dom/Attribute.h"
36 #include "core/dom/ElementTraversal.h"
37 #include "core/dom/NodeListsNodeData.h"
38 #include "core/html/HTMLTableCaptionElement.h"
39 #include "core/html/HTMLTableCellElement.h"
40 #include "core/html/HTMLTableRowElement.h"
41 #include "core/html/HTMLTableRowsCollection.h"
42 #include "core/html/HTMLTableSectionElement.h"
43 #include "core/html/parser/HTMLParserIdioms.h"
44 #include "core/layout/LayoutTable.h"
45 #include "platform/weborigin/Referrer.h"
46 #include "wtf/StdLibExtras.h"
50 using namespace HTMLNames
;
52 inline HTMLTableElement::HTMLTableElement(Document
& document
)
53 : HTMLElement(tableTag
, document
)
55 , m_borderColorAttr(false)
57 , m_rulesAttr(UnsetRules
)
62 // An explicit empty destructor should be in HTMLTableElement.cpp, because
63 // if an implicit destructor is used or an empty destructor is defined in
64 // HTMLTableElement.h, when including HTMLTableElement, msvc tries to expand
65 // the destructor and causes a compile error because of lack of
66 // StylePropertySet definition.
67 HTMLTableElement::~HTMLTableElement()
71 DEFINE_NODE_FACTORY(HTMLTableElement
)
73 HTMLTableCaptionElement
* HTMLTableElement::caption() const
75 return Traversal
<HTMLTableCaptionElement
>::firstChild(*this);
78 void HTMLTableElement::setCaption(PassRefPtrWillBeRawPtr
<HTMLTableCaptionElement
> newCaption
, ExceptionState
& exceptionState
)
81 insertBefore(newCaption
, firstChild(), exceptionState
);
84 HTMLTableSectionElement
* HTMLTableElement::tHead() const
86 return toHTMLTableSectionElement(Traversal
<HTMLElement
>::firstChild(*this, HasHTMLTagName(theadTag
)));
89 void HTMLTableElement::setTHead(PassRefPtrWillBeRawPtr
<HTMLTableSectionElement
> newHead
, ExceptionState
& exceptionState
)
94 for (child
= Traversal
<HTMLElement
>::firstChild(*this); child
; child
= Traversal
<HTMLElement
>::nextSibling(*child
)) {
95 if (!child
->hasTagName(captionTag
) && !child
->hasTagName(colgroupTag
))
99 insertBefore(newHead
, child
, exceptionState
);
102 HTMLTableSectionElement
* HTMLTableElement::tFoot() const
104 return toHTMLTableSectionElement(Traversal
<HTMLElement
>::firstChild(*this, HasHTMLTagName(tfootTag
)));
107 void HTMLTableElement::setTFoot(PassRefPtrWillBeRawPtr
<HTMLTableSectionElement
> newFoot
, ExceptionState
& exceptionState
)
112 for (child
= Traversal
<HTMLElement
>::firstChild(*this); child
; child
= Traversal
<HTMLElement
>::nextSibling(*child
)) {
113 if (!child
->hasTagName(captionTag
) && !child
->hasTagName(colgroupTag
) && !child
->hasTagName(theadTag
))
117 insertBefore(newFoot
, child
, exceptionState
);
120 PassRefPtrWillBeRawPtr
<HTMLElement
> HTMLTableElement::createTHead()
122 if (HTMLTableSectionElement
* existingHead
= tHead())
124 RefPtrWillBeRawPtr
<HTMLTableSectionElement
> head
= HTMLTableSectionElement::create(theadTag
, document());
125 setTHead(head
, IGNORE_EXCEPTION
);
126 return head
.release();
129 void HTMLTableElement::deleteTHead()
131 removeChild(tHead(), IGNORE_EXCEPTION
);
134 PassRefPtrWillBeRawPtr
<HTMLElement
> HTMLTableElement::createTFoot()
136 if (HTMLTableSectionElement
* existingFoot
= tFoot())
138 RefPtrWillBeRawPtr
<HTMLTableSectionElement
> foot
= HTMLTableSectionElement::create(tfootTag
, document());
139 setTFoot(foot
, IGNORE_EXCEPTION
);
140 return foot
.release();
143 void HTMLTableElement::deleteTFoot()
145 removeChild(tFoot(), IGNORE_EXCEPTION
);
148 PassRefPtrWillBeRawPtr
<HTMLElement
> HTMLTableElement::createTBody()
150 RefPtrWillBeRawPtr
<HTMLTableSectionElement
> body
= HTMLTableSectionElement::create(tbodyTag
, document());
151 Node
* referenceElement
= lastBody() ? lastBody()->nextSibling() : 0;
153 insertBefore(body
, referenceElement
);
154 return body
.release();
157 PassRefPtrWillBeRawPtr
<HTMLElement
> HTMLTableElement::createCaption()
159 if (HTMLTableCaptionElement
* existingCaption
= caption())
160 return existingCaption
;
161 RefPtrWillBeRawPtr
<HTMLTableCaptionElement
> caption
= HTMLTableCaptionElement::create(document());
162 setCaption(caption
, IGNORE_EXCEPTION
);
163 return caption
.release();
166 void HTMLTableElement::deleteCaption()
168 removeChild(caption(), IGNORE_EXCEPTION
);
171 HTMLTableSectionElement
* HTMLTableElement::lastBody() const
173 return toHTMLTableSectionElement(Traversal
<HTMLElement
>::lastChild(*this, HasHTMLTagName(tbodyTag
)));
176 PassRefPtrWillBeRawPtr
<HTMLElement
> HTMLTableElement::insertRow(int index
, ExceptionState
& exceptionState
)
179 exceptionState
.throwDOMException(IndexSizeError
, "The index provided (" + String::number(index
) + ") is less than -1.");
183 RefPtrWillBeRawPtr
<Node
> protectFromMutationEvents(this);
185 RefPtrWillBeRawPtr
<HTMLTableRowElement
> lastRow
= nullptr;
186 RefPtrWillBeRawPtr
<HTMLTableRowElement
> row
= nullptr;
188 lastRow
= HTMLTableRowsCollection::lastRow(*this);
190 for (int i
= 0; i
<= index
; ++i
) {
191 row
= HTMLTableRowsCollection::rowAfter(*this, lastRow
.get());
194 exceptionState
.throwDOMException(IndexSizeError
, "The index provided (" + String::number(index
) + ") is greater than the number of rows in the table (" + String::number(i
) + ").");
203 RefPtrWillBeRawPtr
<ContainerNode
> parent
;
205 parent
= row
? row
->parentNode() : lastRow
->parentNode();
209 RefPtrWillBeRawPtr
<HTMLTableSectionElement
> newBody
= HTMLTableSectionElement::create(tbodyTag
, document());
210 RefPtrWillBeRawPtr
<HTMLTableRowElement
> newRow
= HTMLTableRowElement::create(document());
211 newBody
->appendChild(newRow
, exceptionState
);
212 appendChild(newBody
.release(), exceptionState
);
213 return newRow
.release();
217 RefPtrWillBeRawPtr
<HTMLTableRowElement
> newRow
= HTMLTableRowElement::create(document());
218 parent
->insertBefore(newRow
, row
.get(), exceptionState
);
219 return newRow
.release();
222 void HTMLTableElement::deleteRow(int index
, ExceptionState
& exceptionState
)
225 exceptionState
.throwDOMException(IndexSizeError
, "The index provided (" + String::number(index
) + ") is less than -1.");
229 HTMLTableRowElement
* row
= 0;
232 row
= HTMLTableRowsCollection::lastRow(*this);
234 for (i
= 0; i
<= index
; ++i
) {
235 row
= HTMLTableRowsCollection::rowAfter(*this, row
);
241 exceptionState
.throwDOMException(IndexSizeError
, "The index provided (" + String::number(index
) + ") is greater than the number of rows in the table (" + String::number(i
) + ").");
244 row
->remove(exceptionState
);
247 void HTMLTableElement::setNeedsTableStyleRecalc() const
249 Element
* element
= ElementTraversal::next(*this, this);
251 element
->setNeedsStyleRecalc(LocalStyleChange
, StyleChangeReasonForTracing::fromAttribute(rulesAttr
));
252 if (isHTMLTableCellElement(*element
))
253 element
= ElementTraversal::nextSkippingChildren(*element
, this);
255 element
= ElementTraversal::next(*element
, this);
259 static bool getBordersFromFrameAttributeValue(const AtomicString
& value
, bool& borderTop
, bool& borderRight
, bool& borderBottom
, bool& borderLeft
)
263 borderBottom
= false;
266 if (equalIgnoringCase(value
, "above"))
268 else if (equalIgnoringCase(value
, "below"))
270 else if (equalIgnoringCase(value
, "hsides"))
271 borderTop
= borderBottom
= true;
272 else if (equalIgnoringCase(value
, "vsides"))
273 borderLeft
= borderRight
= true;
274 else if (equalIgnoringCase(value
, "lhs"))
276 else if (equalIgnoringCase(value
, "rhs"))
278 else if (equalIgnoringCase(value
, "box") || equalIgnoringCase(value
, "border"))
279 borderTop
= borderBottom
= borderLeft
= borderRight
= true;
280 else if (!equalIgnoringCase(value
, "void"))
285 void HTMLTableElement::collectStyleForPresentationAttribute(const QualifiedName
& name
, const AtomicString
& value
, MutableStylePropertySet
* style
)
287 if (name
== widthAttr
) {
288 addHTMLLengthToStyle(style
, CSSPropertyWidth
, value
);
289 } else if (name
== heightAttr
) {
290 addHTMLLengthToStyle(style
, CSSPropertyHeight
, value
);
291 } else if (name
== borderAttr
) {
292 addPropertyToPresentationAttributeStyle(style
, CSSPropertyBorderWidth
, parseBorderWidthAttribute(value
), CSSPrimitiveValue::UnitType::Pixels
);
293 } else if (name
== bordercolorAttr
) {
294 if (!value
.isEmpty())
295 addHTMLColorToStyle(style
, CSSPropertyBorderColor
, value
);
296 } else if (name
== bgcolorAttr
) {
297 addHTMLColorToStyle(style
, CSSPropertyBackgroundColor
, value
);
298 } else if (name
== backgroundAttr
) {
299 String url
= stripLeadingAndTrailingHTMLSpaces(value
);
300 if (!url
.isEmpty()) {
301 RefPtrWillBeRawPtr
<CSSImageValue
> imageValue
= CSSImageValue::create(url
, document().completeURL(url
));
302 imageValue
->setReferrer(Referrer(document().outgoingReferrer(), document().referrerPolicy()));
303 style
->setProperty(CSSProperty(CSSPropertyBackgroundImage
, imageValue
.release()));
305 } else if (name
== valignAttr
) {
306 if (!value
.isEmpty())
307 addPropertyToPresentationAttributeStyle(style
, CSSPropertyVerticalAlign
, value
);
308 } else if (name
== cellspacingAttr
) {
309 if (!value
.isEmpty())
310 addHTMLLengthToStyle(style
, CSSPropertyBorderSpacing
, value
);
311 } else if (name
== alignAttr
) {
312 if (!value
.isEmpty()) {
313 if (equalIgnoringCase(value
, "center")) {
314 addPropertyToPresentationAttributeStyle(style
, CSSPropertyWebkitMarginStart
, CSSValueAuto
);
315 addPropertyToPresentationAttributeStyle(style
, CSSPropertyWebkitMarginEnd
, CSSValueAuto
);
317 addPropertyToPresentationAttributeStyle(style
, CSSPropertyFloat
, value
);
320 } else if (name
== rulesAttr
) {
321 // The presence of a valid rules attribute causes border collapsing to be enabled.
322 if (m_rulesAttr
!= UnsetRules
)
323 addPropertyToPresentationAttributeStyle(style
, CSSPropertyBorderCollapse
, CSSValueCollapse
);
324 } else if (name
== frameAttr
) {
329 if (getBordersFromFrameAttributeValue(value
, borderTop
, borderRight
, borderBottom
, borderLeft
)) {
330 addPropertyToPresentationAttributeStyle(style
, CSSPropertyBorderWidth
, CSSValueThin
);
331 addPropertyToPresentationAttributeStyle(style
, CSSPropertyBorderTopStyle
, borderTop
? CSSValueSolid
: CSSValueHidden
);
332 addPropertyToPresentationAttributeStyle(style
, CSSPropertyBorderBottomStyle
, borderBottom
? CSSValueSolid
: CSSValueHidden
);
333 addPropertyToPresentationAttributeStyle(style
, CSSPropertyBorderLeftStyle
, borderLeft
? CSSValueSolid
: CSSValueHidden
);
334 addPropertyToPresentationAttributeStyle(style
, CSSPropertyBorderRightStyle
, borderRight
? CSSValueSolid
: CSSValueHidden
);
337 HTMLElement::collectStyleForPresentationAttribute(name
, value
, style
);
341 bool HTMLTableElement::isPresentationAttribute(const QualifiedName
& name
) const
343 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
)
345 return HTMLElement::isPresentationAttribute(name
);
348 void HTMLTableElement::parseAttribute(const QualifiedName
& name
, const AtomicString
& value
)
350 CellBorders bordersBefore
= cellBorders();
351 unsigned short oldPadding
= m_padding
;
353 if (name
== borderAttr
) {
354 // FIXME: This attribute is a mess.
355 m_borderAttr
= parseBorderWidthAttribute(value
);
356 } else if (name
== bordercolorAttr
) {
357 m_borderColorAttr
= !value
.isEmpty();
358 } else if (name
== frameAttr
) {
359 // FIXME: This attribute is a mess.
364 m_frameAttr
= getBordersFromFrameAttributeValue(value
, borderTop
, borderRight
, borderBottom
, borderLeft
);
365 } else if (name
== rulesAttr
) {
366 m_rulesAttr
= UnsetRules
;
367 if (equalIgnoringCase(value
, "none"))
368 m_rulesAttr
= NoneRules
;
369 else if (equalIgnoringCase(value
, "groups"))
370 m_rulesAttr
= GroupsRules
;
371 else if (equalIgnoringCase(value
, "rows"))
372 m_rulesAttr
= RowsRules
;
373 else if (equalIgnoringCase(value
, "cols"))
374 m_rulesAttr
= ColsRules
;
375 else if (equalIgnoringCase(value
, "all"))
376 m_rulesAttr
= AllRules
;
377 } else if (name
== cellpaddingAttr
) {
378 if (!value
.isEmpty())
379 m_padding
= max(0, value
.toInt());
382 } else if (name
== colsAttr
) {
385 HTMLElement::parseAttribute(name
, value
);
388 if (bordersBefore
!= cellBorders() || oldPadding
!= m_padding
) {
389 m_sharedCellStyle
= nullptr;
390 setNeedsTableStyleRecalc();
394 static PassRefPtrWillBeRawPtr
<StylePropertySet
> createBorderStyle(CSSValueID value
)
396 RefPtrWillBeRawPtr
<MutableStylePropertySet
> style
= MutableStylePropertySet::create(HTMLQuirksMode
);
397 style
->setProperty(CSSPropertyBorderTopStyle
, value
);
398 style
->setProperty(CSSPropertyBorderBottomStyle
, value
);
399 style
->setProperty(CSSPropertyBorderLeftStyle
, value
);
400 style
->setProperty(CSSPropertyBorderRightStyle
, value
);
401 return style
.release();
404 const StylePropertySet
* HTMLTableElement::additionalPresentationAttributeStyle()
409 if (!m_borderAttr
&& !m_borderColorAttr
) {
410 // Setting the border to 'hidden' allows it to win over any border
411 // set on the table's cells during border-conflict resolution.
412 if (m_rulesAttr
!= UnsetRules
) {
413 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(StylePropertySet
, solidBorderStyle
, (createBorderStyle(CSSValueHidden
)));
414 return solidBorderStyle
;
419 if (m_borderColorAttr
) {
420 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(StylePropertySet
, solidBorderStyle
, (createBorderStyle(CSSValueSolid
)));
421 return solidBorderStyle
;
423 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(StylePropertySet
, outsetBorderStyle
, (createBorderStyle(CSSValueOutset
)));
424 return outsetBorderStyle
;
427 HTMLTableElement::CellBorders
HTMLTableElement::cellBorders() const
429 switch (m_rulesAttr
) {
436 return SolidBordersColsOnly
;
438 return SolidBordersRowsOnly
;
442 if (m_borderColorAttr
)
446 ASSERT_NOT_REACHED();
450 PassRefPtrWillBeRawPtr
<StylePropertySet
> HTMLTableElement::createSharedCellStyle()
452 RefPtrWillBeRawPtr
<MutableStylePropertySet
> style
= MutableStylePropertySet::create(HTMLQuirksMode
);
454 switch (cellBorders()) {
455 case SolidBordersColsOnly
:
456 style
->setProperty(CSSPropertyBorderLeftWidth
, CSSValueThin
);
457 style
->setProperty(CSSPropertyBorderRightWidth
, CSSValueThin
);
458 style
->setProperty(CSSPropertyBorderLeftStyle
, CSSValueSolid
);
459 style
->setProperty(CSSPropertyBorderRightStyle
, CSSValueSolid
);
460 style
->setProperty(CSSPropertyBorderColor
, cssValuePool().createInheritedValue());
462 case SolidBordersRowsOnly
:
463 style
->setProperty(CSSPropertyBorderTopWidth
, CSSValueThin
);
464 style
->setProperty(CSSPropertyBorderBottomWidth
, CSSValueThin
);
465 style
->setProperty(CSSPropertyBorderTopStyle
, CSSValueSolid
);
466 style
->setProperty(CSSPropertyBorderBottomStyle
, CSSValueSolid
);
467 style
->setProperty(CSSPropertyBorderColor
, cssValuePool().createInheritedValue());
470 style
->setProperty(CSSPropertyBorderWidth
, cssValuePool().createValue(1, CSSPrimitiveValue::UnitType::Pixels
));
471 style
->setProperty(CSSPropertyBorderStyle
, cssValuePool().createIdentifierValue(CSSValueSolid
));
472 style
->setProperty(CSSPropertyBorderColor
, cssValuePool().createInheritedValue());
475 style
->setProperty(CSSPropertyBorderWidth
, cssValuePool().createValue(1, CSSPrimitiveValue::UnitType::Pixels
));
476 style
->setProperty(CSSPropertyBorderStyle
, cssValuePool().createIdentifierValue(CSSValueInset
));
477 style
->setProperty(CSSPropertyBorderColor
, cssValuePool().createInheritedValue());
480 // If 'rules=none' then allow any borders set at cell level to take effect.
485 style
->setProperty(CSSPropertyPadding
, cssValuePool().createValue(m_padding
, CSSPrimitiveValue::UnitType::Pixels
));
487 return style
.release();
490 const StylePropertySet
* HTMLTableElement::additionalCellStyle()
492 if (!m_sharedCellStyle
)
493 m_sharedCellStyle
= createSharedCellStyle();
494 return m_sharedCellStyle
.get();
497 static PassRefPtrWillBeRawPtr
<StylePropertySet
> createGroupBorderStyle(int rows
)
499 RefPtrWillBeRawPtr
<MutableStylePropertySet
> style
= MutableStylePropertySet::create(HTMLQuirksMode
);
501 style
->setProperty(CSSPropertyBorderTopWidth
, CSSValueThin
);
502 style
->setProperty(CSSPropertyBorderBottomWidth
, CSSValueThin
);
503 style
->setProperty(CSSPropertyBorderTopStyle
, CSSValueSolid
);
504 style
->setProperty(CSSPropertyBorderBottomStyle
, CSSValueSolid
);
506 style
->setProperty(CSSPropertyBorderLeftWidth
, CSSValueThin
);
507 style
->setProperty(CSSPropertyBorderRightWidth
, CSSValueThin
);
508 style
->setProperty(CSSPropertyBorderLeftStyle
, CSSValueSolid
);
509 style
->setProperty(CSSPropertyBorderRightStyle
, CSSValueSolid
);
511 return style
.release();
514 const StylePropertySet
* HTMLTableElement::additionalGroupStyle(bool rows
)
516 if (m_rulesAttr
!= GroupsRules
)
520 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(StylePropertySet
, rowBorderStyle
, (createGroupBorderStyle(true)));
521 return rowBorderStyle
;
523 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(StylePropertySet
, columnBorderStyle
, (createGroupBorderStyle(false)));
524 return columnBorderStyle
;
527 bool HTMLTableElement::isURLAttribute(const Attribute
& attribute
) const
529 return attribute
.name() == backgroundAttr
|| HTMLElement::isURLAttribute(attribute
);
532 bool HTMLTableElement::hasLegalLinkAttribute(const QualifiedName
& name
) const
534 return name
== backgroundAttr
|| HTMLElement::hasLegalLinkAttribute(name
);
537 const QualifiedName
& HTMLTableElement::subResourceAttributeName() const
539 return backgroundAttr
;
542 PassRefPtrWillBeRawPtr
<HTMLTableRowsCollection
> HTMLTableElement::rows()
544 return ensureCachedCollection
<HTMLTableRowsCollection
>(TableRows
);
547 PassRefPtrWillBeRawPtr
<HTMLCollection
> HTMLTableElement::tBodies()
549 return ensureCachedCollection
<HTMLCollection
>(TableTBodies
);
552 const AtomicString
& HTMLTableElement::rules() const
554 return getAttribute(rulesAttr
);
557 const AtomicString
& HTMLTableElement::summary() const
559 return getAttribute(summaryAttr
);
562 DEFINE_TRACE(HTMLTableElement
)
564 visitor
->trace(m_sharedCellStyle
);
565 HTMLElement::trace(visitor
);