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