Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLTableElement.cpp
blob088167c56f992f5afe8d0380648e4d8d9808df39
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.
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.
25 #include "config.h"
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"
48 namespace blink {
50 using namespace HTMLNames;
52 inline HTMLTableElement::HTMLTableElement(Document& document)
53 : HTMLElement(tableTag, document)
54 , m_borderAttr(false)
55 , m_borderColorAttr(false)
56 , m_frameAttr(false)
57 , m_rulesAttr(UnsetRules)
58 , m_padding(1)
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)
80 deleteCaption();
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)
91 deleteTHead();
93 HTMLElement* child;
94 for (child = Traversal<HTMLElement>::firstChild(*this); child; child = Traversal<HTMLElement>::nextSibling(*child)) {
95 if (!child->hasTagName(captionTag) && !child->hasTagName(colgroupTag))
96 break;
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)
109 deleteTFoot();
111 HTMLElement* child;
112 for (child = Traversal<HTMLElement>::firstChild(*this); child; child = Traversal<HTMLElement>::nextSibling(*child)) {
113 if (!child->hasTagName(captionTag) && !child->hasTagName(colgroupTag) && !child->hasTagName(theadTag))
114 break;
117 insertBefore(newFoot, child, exceptionState);
120 PassRefPtrWillBeRawPtr<HTMLElement> HTMLTableElement::createTHead()
122 if (HTMLTableSectionElement* existingHead = tHead())
123 return existingHead;
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())
137 return existingFoot;
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)
178 if (index < -1) {
179 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is less than -1.");
180 return nullptr;
183 RefPtrWillBeRawPtr<Node> protectFromMutationEvents(this);
185 RefPtrWillBeRawPtr<HTMLTableRowElement> lastRow = nullptr;
186 RefPtrWillBeRawPtr<HTMLTableRowElement> row = nullptr;
187 if (index == -1) {
188 lastRow = HTMLTableRowsCollection::lastRow(*this);
189 } else {
190 for (int i = 0; i <= index; ++i) {
191 row = HTMLTableRowsCollection::rowAfter(*this, lastRow.get());
192 if (!row) {
193 if (i != index) {
194 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than the number of rows in the table (" + String::number(i) + ").");
195 return nullptr;
197 break;
199 lastRow = row;
203 RefPtrWillBeRawPtr<ContainerNode> parent;
204 if (lastRow) {
205 parent = row ? row->parentNode() : lastRow->parentNode();
206 } else {
207 parent = lastBody();
208 if (!parent) {
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)
224 if (index < -1) {
225 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is less than -1.");
226 return;
229 HTMLTableRowElement* row = 0;
230 int i = 0;
231 if (index == -1) {
232 row = HTMLTableRowsCollection::lastRow(*this);
233 } else {
234 for (i = 0; i <= index; ++i) {
235 row = HTMLTableRowsCollection::rowAfter(*this, row);
236 if (!row)
237 break;
240 if (!row) {
241 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than the number of rows in the table (" + String::number(i) + ").");
242 return;
244 row->remove(exceptionState);
247 void HTMLTableElement::setNeedsTableStyleRecalc() const
249 Element* element = ElementTraversal::next(*this, this);
250 while (element) {
251 element->setNeedsStyleRecalc(LocalStyleChange, StyleChangeReasonForTracing::fromAttribute(rulesAttr));
252 if (isHTMLTableCellElement(*element))
253 element = ElementTraversal::nextSkippingChildren(*element, this);
254 else
255 element = ElementTraversal::next(*element, this);
259 static bool getBordersFromFrameAttributeValue(const AtomicString& value, bool& borderTop, bool& borderRight, bool& borderBottom, bool& borderLeft)
261 borderTop = false;
262 borderRight = false;
263 borderBottom = false;
264 borderLeft = false;
266 if (equalIgnoringCase(value, "above"))
267 borderTop = true;
268 else if (equalIgnoringCase(value, "below"))
269 borderBottom = true;
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"))
275 borderLeft = true;
276 else if (equalIgnoringCase(value, "rhs"))
277 borderRight = true;
278 else if (equalIgnoringCase(value, "box") || equalIgnoringCase(value, "border"))
279 borderTop = borderBottom = borderLeft = borderRight = true;
280 else if (!equalIgnoringCase(value, "void"))
281 return false;
282 return true;
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);
316 } else {
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) {
325 bool borderTop;
326 bool borderRight;
327 bool borderBottom;
328 bool borderLeft;
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);
336 } else {
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)
344 return true;
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.
360 bool borderTop;
361 bool borderRight;
362 bool borderBottom;
363 bool borderLeft;
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());
380 else
381 m_padding = 1;
382 } else if (name == colsAttr) {
383 // ###
384 } else {
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()
406 if (m_frameAttr)
407 return nullptr;
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;
416 return nullptr;
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) {
430 case NoneRules:
431 case GroupsRules:
432 return NoBorders;
433 case AllRules:
434 return SolidBorders;
435 case ColsRules:
436 return SolidBordersColsOnly;
437 case RowsRules:
438 return SolidBordersRowsOnly;
439 case UnsetRules:
440 if (!m_borderAttr)
441 return NoBorders;
442 if (m_borderColorAttr)
443 return SolidBorders;
444 return InsetBorders;
446 ASSERT_NOT_REACHED();
447 return NoBorders;
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());
461 break;
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());
468 break;
469 case SolidBorders:
470 style->setProperty(CSSPropertyBorderWidth, cssValuePool().createValue(1, CSSPrimitiveValue::UnitType::Pixels));
471 style->setProperty(CSSPropertyBorderStyle, cssValuePool().createIdentifierValue(CSSValueSolid));
472 style->setProperty(CSSPropertyBorderColor, cssValuePool().createInheritedValue());
473 break;
474 case InsetBorders:
475 style->setProperty(CSSPropertyBorderWidth, cssValuePool().createValue(1, CSSPrimitiveValue::UnitType::Pixels));
476 style->setProperty(CSSPropertyBorderStyle, cssValuePool().createIdentifierValue(CSSValueInset));
477 style->setProperty(CSSPropertyBorderColor, cssValuePool().createInheritedValue());
478 break;
479 case NoBorders:
480 // If 'rules=none' then allow any borders set at cell level to take effect.
481 break;
484 if (m_padding)
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);
500 if (rows) {
501 style->setProperty(CSSPropertyBorderTopWidth, CSSValueThin);
502 style->setProperty(CSSPropertyBorderBottomWidth, CSSValueThin);
503 style->setProperty(CSSPropertyBorderTopStyle, CSSValueSolid);
504 style->setProperty(CSSPropertyBorderBottomStyle, CSSValueSolid);
505 } else {
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)
517 return nullptr;
519 if (rows) {
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);