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 "CSSPropertyNames.h"
29 #include "CSSValueKeywords.h"
30 #include "HTMLNames.h"
31 #include "bindings/v8/ExceptionState.h"
32 #include "bindings/v8/ExceptionStatePlaceholder.h"
33 #include "core/css/CSSImageValue.h"
34 #include "core/css/CSSValuePool.h"
35 #include "core/css/StylePropertySet.h"
36 #include "core/dom/Attribute.h"
37 #include "core/dom/ElementTraversal.h"
38 #include "core/dom/ExceptionCode.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/html/HTMLTableCaptionElement.h"
41 #include "core/html/HTMLTableCellElement.h"
42 #include "core/html/HTMLTableRowElement.h"
43 #include "core/html/HTMLTableRowsCollection.h"
44 #include "core/html/HTMLTableSectionElement.h"
45 #include "core/html/parser/HTMLParserIdioms.h"
46 #include "core/rendering/RenderTable.h"
47 #include "wtf/StdLibExtras.h"
51 using namespace HTMLNames;
53 HTMLTableElement::HTMLTableElement(Document& document)
54 : HTMLElement(tableTag, document)
56 , m_borderColorAttr(false)
58 , m_rulesAttr(UnsetRules)
61 ScriptWrappable::init(this);
64 PassRefPtrWillBeRawPtr<HTMLTableElement> HTMLTableElement::create(Document& document)
66 return adoptRefWillBeRefCountedGarbageCollected(new HTMLTableElement(document));
69 HTMLTableCaptionElement* HTMLTableElement::caption() const
71 return Traversal<HTMLTableCaptionElement>::firstChild(*this);
74 void HTMLTableElement::setCaption(PassRefPtrWillBeRawPtr<HTMLTableCaptionElement> newCaption, ExceptionState& exceptionState)
77 insertBefore(newCaption, firstChild(), exceptionState);
80 HTMLTableSectionElement* HTMLTableElement::tHead() const
82 for (Element* child = ElementTraversal::firstWithin(*this); child; child = ElementTraversal::nextSibling(*child)) {
83 if (child->hasTagName(theadTag))
84 return toHTMLTableSectionElement(child);
89 void HTMLTableElement::setTHead(PassRefPtrWillBeRawPtr<HTMLTableSectionElement> newHead, ExceptionState& exceptionState)
94 for (child = ElementTraversal::firstWithin(*this); child; child = ElementTraversal::nextSibling(*child)) {
95 if (!child->hasTagName(captionTag) && !child->hasTagName(colgroupTag))
99 insertBefore(newHead, child, exceptionState);
102 HTMLTableSectionElement* HTMLTableElement::tFoot() const
104 for (Element* child = ElementTraversal::firstWithin(*this); child; child = ElementTraversal::nextSibling(*child)) {
105 if (child->hasTagName(tfootTag))
106 return toHTMLTableSectionElement(child);
111 void HTMLTableElement::setTFoot(PassRefPtrWillBeRawPtr<HTMLTableSectionElement> newFoot, ExceptionState& exceptionState)
116 for (child = ElementTraversal::firstWithin(*this); child; child = ElementTraversal::nextSibling(*child)) {
117 if (!child->hasTagName(captionTag) && !child->hasTagName(colgroupTag) && !child->hasTagName(theadTag))
121 insertBefore(newFoot, child, exceptionState);
124 PassRefPtrWillBeRawPtr<HTMLElement> HTMLTableElement::createTHead()
126 if (HTMLTableSectionElement* existingHead = tHead())
128 RefPtrWillBeRawPtr<HTMLTableSectionElement> head = HTMLTableSectionElement::create(theadTag, document());
129 setTHead(head, IGNORE_EXCEPTION);
130 return head.release();
133 void HTMLTableElement::deleteTHead()
135 removeChild(tHead(), IGNORE_EXCEPTION);
138 PassRefPtrWillBeRawPtr<HTMLElement> HTMLTableElement::createTFoot()
140 if (HTMLTableSectionElement* existingFoot = tFoot())
142 RefPtrWillBeRawPtr<HTMLTableSectionElement> foot = HTMLTableSectionElement::create(tfootTag, document());
143 setTFoot(foot, IGNORE_EXCEPTION);
144 return foot.release();
147 void HTMLTableElement::deleteTFoot()
149 removeChild(tFoot(), IGNORE_EXCEPTION);
152 PassRefPtrWillBeRawPtr<HTMLElement> HTMLTableElement::createTBody()
154 RefPtrWillBeRawPtr<HTMLTableSectionElement> body = HTMLTableSectionElement::create(tbodyTag, document());
155 Node* referenceElement = lastBody() ? lastBody()->nextSibling() : 0;
157 insertBefore(body, referenceElement);
158 return body.release();
161 PassRefPtrWillBeRawPtr<HTMLElement> HTMLTableElement::createCaption()
163 if (HTMLTableCaptionElement* existingCaption = caption())
164 return existingCaption;
165 RefPtrWillBeRawPtr<HTMLTableCaptionElement> caption = HTMLTableCaptionElement::create(document());
166 setCaption(caption, IGNORE_EXCEPTION);
167 return caption.release();
170 void HTMLTableElement::deleteCaption()
172 removeChild(caption(), IGNORE_EXCEPTION);
175 HTMLTableSectionElement* HTMLTableElement::lastBody() const
177 for (Node* child = lastChild(); child; child = child->previousSibling()) {
178 if (child->hasTagName(tbodyTag))
179 return toHTMLTableSectionElement(child);
184 PassRefPtrWillBeRawPtr<HTMLElement> HTMLTableElement::insertRow(ExceptionState& exceptionState)
186 // The default 'index' argument value is -1.
187 return insertRow(-1, exceptionState);
190 PassRefPtrWillBeRawPtr<HTMLElement> HTMLTableElement::insertRow(int index, ExceptionState& exceptionState)
193 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is less than -1.");
197 RefPtrWillBeRawPtr<Node> protectFromMutationEvents(this);
199 RefPtrWillBeRawPtr<HTMLTableRowElement> lastRow = nullptr;
200 RefPtrWillBeRawPtr<HTMLTableRowElement> row = nullptr;
202 lastRow = HTMLTableRowsCollection::lastRow(*this);
204 for (int i = 0; i <= index; ++i) {
205 row = HTMLTableRowsCollection::rowAfter(*this, lastRow.get());
208 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than the number of rows in the table (" + String::number(i) + ").");
217 RefPtr<ContainerNode> parent;
219 parent = row ? row->parentNode() : lastRow->parentNode();
223 RefPtrWillBeRawPtr<HTMLTableSectionElement> newBody = HTMLTableSectionElement::create(tbodyTag, document());
224 RefPtrWillBeRawPtr<HTMLTableRowElement> newRow = HTMLTableRowElement::create(document());
225 newBody->appendChild(newRow, exceptionState);
226 appendChild(newBody.release(), exceptionState);
227 return newRow.release();
231 RefPtrWillBeRawPtr<HTMLTableRowElement> newRow = HTMLTableRowElement::create(document());
232 parent->insertBefore(newRow, row.get(), exceptionState);
233 return newRow.release();
236 void HTMLTableElement::deleteRow(int index, ExceptionState& exceptionState)
239 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is less than -1.");
243 HTMLTableRowElement* row = 0;
246 row = HTMLTableRowsCollection::lastRow(*this);
248 for (i = 0; i <= index; ++i) {
249 row = HTMLTableRowsCollection::rowAfter(*this, row);
255 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than the number of rows in the table (" + String::number(i) + ").");
258 row->remove(exceptionState);
261 void HTMLTableElement::setNeedsTableStyleRecalc() const
263 Element* element = ElementTraversal::next(*this, this);
265 element->setNeedsStyleRecalc(LocalStyleChange);
266 if (isHTMLTableCellElement(*element))
267 element = ElementTraversal::nextSkippingChildren(*element, this);
269 element = ElementTraversal::next(*element, this);
273 static bool getBordersFromFrameAttributeValue(const AtomicString& value, bool& borderTop, bool& borderRight, bool& borderBottom, bool& borderLeft)
277 borderBottom = false;
280 if (equalIgnoringCase(value, "above"))
282 else if (equalIgnoringCase(value, "below"))
284 else if (equalIgnoringCase(value, "hsides"))
285 borderTop = borderBottom = true;
286 else if (equalIgnoringCase(value, "vsides"))
287 borderLeft = borderRight = true;
288 else if (equalIgnoringCase(value, "lhs"))
290 else if (equalIgnoringCase(value, "rhs"))
292 else if (equalIgnoringCase(value, "box") || equalIgnoringCase(value, "border"))
293 borderTop = borderBottom = borderLeft = borderRight = true;
294 else if (!equalIgnoringCase(value, "void"))
299 void HTMLTableElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
301 if (name == widthAttr)
302 addHTMLLengthToStyle(style, CSSPropertyWidth, value);
303 else if (name == heightAttr)
304 addHTMLLengthToStyle(style, CSSPropertyHeight, value);
305 else if (name == borderAttr)
306 addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderWidth, parseBorderWidthAttribute(value), CSSPrimitiveValue::CSS_PX);
307 else if (name == bordercolorAttr) {
308 if (!value.isEmpty())
309 addHTMLColorToStyle(style, CSSPropertyBorderColor, value);
310 } else if (name == bgcolorAttr)
311 addHTMLColorToStyle(style, CSSPropertyBackgroundColor, value);
312 else if (name == backgroundAttr) {
313 String url = stripLeadingAndTrailingHTMLSpaces(value);
314 if (!url.isEmpty()) {
315 RefPtrWillBeRawPtr<CSSImageValue> imageValue = CSSImageValue::create(url, document().completeURL(url));
316 imageValue->setReferrer(document().outgoingReferrer());
317 style->setProperty(CSSProperty(CSSPropertyBackgroundImage, imageValue.release()));
319 } else if (name == valignAttr) {
320 if (!value.isEmpty())
321 addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, value);
322 } else if (name == cellspacingAttr) {
323 if (!value.isEmpty())
324 addHTMLLengthToStyle(style, CSSPropertyBorderSpacing, value);
325 } else if (name == vspaceAttr) {
326 UseCounter::count(document(), UseCounter::HTMLTableElementVspace);
327 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
328 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
329 } else if (name == hspaceAttr) {
330 UseCounter::count(document(), UseCounter::HTMLTableElementHspace);
331 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
332 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
333 } else if (name == alignAttr) {
334 if (!value.isEmpty()) {
335 if (equalIgnoringCase(value, "center")) {
336 addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitMarginStart, CSSValueAuto);
337 addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitMarginEnd, CSSValueAuto);
339 addPropertyToPresentationAttributeStyle(style, CSSPropertyFloat, value);
341 } else if (name == rulesAttr) {
342 // The presence of a valid rules attribute causes border collapsing to be enabled.
343 if (m_rulesAttr != UnsetRules)
344 addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderCollapse, CSSValueCollapse);
345 } else if (name == frameAttr) {
350 if (getBordersFromFrameAttributeValue(value, borderTop, borderRight, borderBottom, borderLeft)) {
351 addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderWidth, CSSValueThin);
352 addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderTopStyle, borderTop ? CSSValueSolid : CSSValueHidden);
353 addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderBottomStyle, borderBottom ? CSSValueSolid : CSSValueHidden);
354 addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderLeftStyle, borderLeft ? CSSValueSolid : CSSValueHidden);
355 addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderRightStyle, borderRight ? CSSValueSolid : CSSValueHidden);
358 HTMLElement::collectStyleForPresentationAttribute(name, value, style);
361 bool HTMLTableElement::isPresentationAttribute(const QualifiedName& name) const
363 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)
365 return HTMLElement::isPresentationAttribute(name);
368 void HTMLTableElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
370 CellBorders bordersBefore = cellBorders();
371 unsigned short oldPadding = m_padding;
373 if (name == borderAttr) {
374 // FIXME: This attribute is a mess.
375 m_borderAttr = parseBorderWidthAttribute(value);
376 } else if (name == bordercolorAttr) {
377 m_borderColorAttr = !value.isEmpty();
378 } else if (name == frameAttr) {
379 // FIXME: This attribute is a mess.
384 m_frameAttr = getBordersFromFrameAttributeValue(value, borderTop, borderRight, borderBottom, borderLeft);
385 } else if (name == rulesAttr) {
386 m_rulesAttr = UnsetRules;
387 if (equalIgnoringCase(value, "none"))
388 m_rulesAttr = NoneRules;
389 else if (equalIgnoringCase(value, "groups"))
390 m_rulesAttr = GroupsRules;
391 else if (equalIgnoringCase(value, "rows"))
392 m_rulesAttr = RowsRules;
393 else if (equalIgnoringCase(value, "cols"))
394 m_rulesAttr = ColsRules;
395 else if (equalIgnoringCase(value, "all"))
396 m_rulesAttr = AllRules;
397 } else if (name == cellpaddingAttr) {
398 if (!value.isEmpty())
399 m_padding = max(0, value.toInt());
402 } else if (name == colsAttr) {
405 HTMLElement::parseAttribute(name, value);
407 if (bordersBefore != cellBorders() || oldPadding != m_padding) {
408 m_sharedCellStyle = nullptr;
409 setNeedsTableStyleRecalc();
413 static PassRefPtr<StylePropertySet> createBorderStyle(CSSValueID value)
415 RefPtrWillBeRawPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
416 style->setProperty(CSSPropertyBorderTopStyle, value);
417 style->setProperty(CSSPropertyBorderBottomStyle, value);
418 style->setProperty(CSSPropertyBorderLeftStyle, value);
419 style->setProperty(CSSPropertyBorderRightStyle, value);
420 return style.release();
423 const StylePropertySet* HTMLTableElement::additionalPresentationAttributeStyle()
428 if (!m_borderAttr && !m_borderColorAttr) {
429 // Setting the border to 'hidden' allows it to win over any border
430 // set on the table's cells during border-conflict resolution.
431 if (m_rulesAttr != UnsetRules) {
432 DEFINE_STATIC_REF(StylePropertySet, solidBorderStyle, (createBorderStyle(CSSValueHidden)));
433 return solidBorderStyle;
438 if (m_borderColorAttr) {
439 DEFINE_STATIC_REF(StylePropertySet, solidBorderStyle, (createBorderStyle(CSSValueSolid)));
440 return solidBorderStyle;
442 DEFINE_STATIC_REF(StylePropertySet, outsetBorderStyle, (createBorderStyle(CSSValueOutset)));
443 return outsetBorderStyle;
446 HTMLTableElement::CellBorders HTMLTableElement::cellBorders() const
448 switch (m_rulesAttr) {
455 return SolidBordersColsOnly;
457 return SolidBordersRowsOnly;
461 if (m_borderColorAttr)
465 ASSERT_NOT_REACHED();
469 PassRefPtrWillBeRawPtr<StylePropertySet> HTMLTableElement::createSharedCellStyle()
471 RefPtrWillBeRawPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
473 switch (cellBorders()) {
474 case SolidBordersColsOnly:
475 style->setProperty(CSSPropertyBorderLeftWidth, CSSValueThin);
476 style->setProperty(CSSPropertyBorderRightWidth, CSSValueThin);
477 style->setProperty(CSSPropertyBorderLeftStyle, CSSValueSolid);
478 style->setProperty(CSSPropertyBorderRightStyle, CSSValueSolid);
479 style->setProperty(CSSPropertyBorderColor, cssValuePool().createInheritedValue());
481 case SolidBordersRowsOnly:
482 style->setProperty(CSSPropertyBorderTopWidth, CSSValueThin);
483 style->setProperty(CSSPropertyBorderBottomWidth, CSSValueThin);
484 style->setProperty(CSSPropertyBorderTopStyle, CSSValueSolid);
485 style->setProperty(CSSPropertyBorderBottomStyle, CSSValueSolid);
486 style->setProperty(CSSPropertyBorderColor, cssValuePool().createInheritedValue());
489 style->setProperty(CSSPropertyBorderWidth, cssValuePool().createValue(1, CSSPrimitiveValue::CSS_PX));
490 style->setProperty(CSSPropertyBorderStyle, cssValuePool().createIdentifierValue(CSSValueSolid));
491 style->setProperty(CSSPropertyBorderColor, cssValuePool().createInheritedValue());
494 style->setProperty(CSSPropertyBorderWidth, cssValuePool().createValue(1, CSSPrimitiveValue::CSS_PX));
495 style->setProperty(CSSPropertyBorderStyle, cssValuePool().createIdentifierValue(CSSValueInset));
496 style->setProperty(CSSPropertyBorderColor, cssValuePool().createInheritedValue());
499 // If 'rules=none' then allow any borders set at cell level to take effect.
504 style->setProperty(CSSPropertyPadding, cssValuePool().createValue(m_padding, CSSPrimitiveValue::CSS_PX));
506 return style.release();
509 const StylePropertySet* HTMLTableElement::additionalCellStyle()
511 if (!m_sharedCellStyle)
512 m_sharedCellStyle = createSharedCellStyle();
513 return m_sharedCellStyle.get();
516 static PassRefPtrWillBeRawPtr<StylePropertySet> createGroupBorderStyle(int rows)
518 RefPtrWillBeRawPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
520 style->setProperty(CSSPropertyBorderTopWidth, CSSValueThin);
521 style->setProperty(CSSPropertyBorderBottomWidth, CSSValueThin);
522 style->setProperty(CSSPropertyBorderTopStyle, CSSValueSolid);
523 style->setProperty(CSSPropertyBorderBottomStyle, CSSValueSolid);
525 style->setProperty(CSSPropertyBorderLeftWidth, CSSValueThin);
526 style->setProperty(CSSPropertyBorderRightWidth, CSSValueThin);
527 style->setProperty(CSSPropertyBorderLeftStyle, CSSValueSolid);
528 style->setProperty(CSSPropertyBorderRightStyle, CSSValueSolid);
530 return style.release();
533 const StylePropertySet* HTMLTableElement::additionalGroupStyle(bool rows)
535 if (m_rulesAttr != GroupsRules)
539 DEFINE_STATIC_REF(StylePropertySet, rowBorderStyle, (createGroupBorderStyle(true)));
540 return rowBorderStyle;
542 DEFINE_STATIC_REF(StylePropertySet, columnBorderStyle, (createGroupBorderStyle(false)));
543 return columnBorderStyle;
546 bool HTMLTableElement::isURLAttribute(const Attribute& attribute) const
548 return attribute.name() == backgroundAttr || HTMLElement::isURLAttribute(attribute);
551 bool HTMLTableElement::hasLegalLinkAttribute(const QualifiedName& name) const
553 return name == backgroundAttr || HTMLElement::hasLegalLinkAttribute(name);
556 const QualifiedName& HTMLTableElement::subResourceAttributeName() const
558 return backgroundAttr;
561 PassRefPtr<HTMLCollection> HTMLTableElement::rows()
563 return ensureCachedHTMLCollection(TableRows);
566 PassRefPtr<HTMLCollection> HTMLTableElement::tBodies()
568 return ensureCachedHTMLCollection(TableTBodies);
571 const AtomicString& HTMLTableElement::rules() const
573 return getAttribute(rulesAttr);
576 const AtomicString& HTMLTableElement::summary() const
578 return getAttribute(summaryAttr);
581 void HTMLTableElement::trace(Visitor* visitor)
583 visitor->trace(m_sharedCellStyle);
584 HTMLElement::trace(visitor);