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