1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2001 Dirk Mueller (mueller (at) kde.org) 5 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 6 * Copyright (C) 2009 Rob Buis (rwlbuis (at) gmail.com) 7 * Copyright (C) 2011 Google 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/HTMLLinkElement.h" 27 28 #include "bindings/v8/ScriptEventListener.h" 29 #include "core/HTMLNames.h" 30 #include "core/css/MediaList.h" 31 #include "core/css/MediaQueryEvaluator.h" 32 #include "core/css/StyleSheetContents.h" 33 #include "core/css/resolver/StyleResolver.h" 34 #include "core/dom/Attribute.h" 35 #include "core/dom/Document.h" 36 #include "core/dom/StyleEngine.h" 37 #include "core/events/Event.h" 38 #include "core/events/EventSender.h" 39 #include "core/fetch/CSSStyleSheetResource.h" 40 #include "core/fetch/FetchRequest.h" 41 #include "core/fetch/ResourceFetcher.h" 42 #include "core/frame/FrameView.h" 43 #include "core/frame/LocalFrame.h" 44 #include "core/frame/csp/ContentSecurityPolicy.h" 45 #include "core/html/LinkManifest.h" 46 #include "core/html/imports/LinkImport.h" 47 #include "core/loader/FrameLoader.h" 48 #include "core/loader/FrameLoaderClient.h" 49 #include "core/rendering/style/StyleInheritedData.h" 50 #include "platform/RuntimeEnabledFeatures.h" 51 #include "wtf/StdLibExtras.h" 52 53 namespace WebCore { 54 55 using namespace HTMLNames; 56 57 template <typename CharacterType> 58 static void parseSizes(const CharacterType* value, unsigned length, Vector<IntSize>& iconSizes) 59 { 60 enum State { 61 ParseStart, 62 ParseWidth, 63 ParseHeight 64 }; 65 int width = 0; 66 unsigned start = 0; 67 unsigned i = 0; 68 State state = ParseStart; 69 bool invalid = false; 70 for (; i < length; ++i) { 71 if (state == ParseWidth) { 72 if (value[i] == 'x' || value[i] == 'X') { 73 if (i == start) { 74 invalid = true; 75 break; 76 } 77 width = charactersToInt(value + start, i - start); 78 start = i + 1; 79 state = ParseHeight; 80 } else if (value[i] < '0' || value[i] > '9') { 81 invalid = true; 82 break; 83 } 84 } else if (state == ParseHeight) { 85 if (value[i] == ' ') { 86 if (i == start) { 87 invalid = true; 88 break; 89 } 90 int height = charactersToInt(value + start, i - start); 91 iconSizes.append(IntSize(width, height)); 92 start = i + 1; 93 state = ParseStart; 94 } else if (value[i] < '0' || value[i] > '9') { 95 invalid = true; 96 break; 97 } 98 } else if (state == ParseStart) { 99 if (value[i] >= '0' && value[i] <= '9') { 100 start = i; 101 state = ParseWidth; 102 } else if (value[i] != ' ') { 103 invalid = true; 104 break; 105 } 106 } 107 } 108 if (invalid || state == ParseWidth || (state == ParseHeight && start == i)) { 109 iconSizes.clear(); 110 return; 111 } 112 if (state == ParseHeight && i > start) { 113 int height = charactersToInt(value + start, i - start); 114 iconSizes.append(IntSize(width, height)); 115 } 116 } 117 118 static LinkEventSender& linkLoadEventSender() 119 { 120 DEFINE_STATIC_LOCAL(LinkEventSender, sharedLoadEventSender, (EventTypeNames::load)); 121 return sharedLoadEventSender; 122 } 123 124 void HTMLLinkElement::parseSizesAttribute(const AtomicString& value, Vector<IntSize>& iconSizes) 125 { 126 ASSERT(iconSizes.isEmpty()); 127 if (value.isEmpty()) 128 return; 129 if (value.is8Bit()) 130 parseSizes(value.characters8(), value.length(), iconSizes); 131 else 132 parseSizes(value.characters16(), value.length(), iconSizes); 133 } 134 135 inline HTMLLinkElement::HTMLLinkElement(Document& document, bool createdByParser) 136 : HTMLElement(linkTag, document) 137 , m_linkLoader(this) 138 , m_sizes(DOMSettableTokenList::create()) 139 , m_createdByParser(createdByParser) 140 , m_isInShadowTree(false) 141 { 142 ScriptWrappable::init(this); 143 } 144 145 PassRefPtrWillBeRawPtr<HTMLLinkElement> HTMLLinkElement::create(Document& document, bool createdByParser) 146 { 147 return adoptRefWillBeNoop(new HTMLLinkElement(document, createdByParser)); 148 } 149 150 HTMLLinkElement::~HTMLLinkElement() 151 { 152 #if !ENABLE(OILPAN) 153 m_link.clear(); 154 155 if (inDocument()) 156 document().styleEngine()->removeStyleSheetCandidateNode(this); 157 #endif 158 159 linkLoadEventSender().cancelEvent(this); 160 } 161 162 void HTMLLinkElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 163 { 164 if (name == relAttr) { 165 m_relAttribute = LinkRelAttribute(value); 166 process(); 167 } else if (name == hrefAttr) { 168 process(); 169 } else if (name == typeAttr) { 170 m_type = value; 171 process(); 172 } else if (name == sizesAttr) { 173 m_sizes->setValue(value); 174 parseSizesAttribute(value, m_iconSizes); 175 process(); 176 } else if (name == mediaAttr) { 177 m_media = value.string().lower(); 178 process(); 179 } else if (name == disabledAttr) { 180 if (LinkStyle* link = linkStyle()) 181 link->setDisabledState(!value.isNull()); 182 } else { 183 if (name == titleAttr) { 184 if (LinkStyle* link = linkStyle()) 185 link->setSheetTitle(value); 186 } 187 188 HTMLElement::parseAttribute(name, value); 189 } 190 } 191 192 bool HTMLLinkElement::shouldLoadLink() 193 { 194 return inDocument(); 195 } 196 197 bool HTMLLinkElement::loadLink(const String& type, const KURL& url) 198 { 199 return m_linkLoader.loadLink(m_relAttribute, fastGetAttribute(HTMLNames::crossoriginAttr), type, url, document()); 200 } 201 202 LinkResource* HTMLLinkElement::linkResourceToProcess() 203 { 204 bool visible = inDocument() && !m_isInShadowTree; 205 if (!visible) { 206 ASSERT(!linkStyle() || !linkStyle()->hasSheet()); 207 return 0; 208 } 209 210 if (!m_link) { 211 if (m_relAttribute.isImport() && RuntimeEnabledFeatures::htmlImportsEnabled()) { 212 m_link = LinkImport::create(this); 213 } else if (m_relAttribute.isManifest() && RuntimeEnabledFeatures::manifestEnabled()) { 214 m_link = LinkManifest::create(this); 215 } else { 216 OwnPtrWillBeRawPtr<LinkStyle> link = LinkStyle::create(this); 217 if (fastHasAttribute(disabledAttr) || m_relAttribute.isTransitionExitingStylesheet()) 218 link->setDisabledState(true); 219 m_link = link.release(); 220 } 221 } 222 223 return m_link.get(); 224 } 225 226 LinkStyle* HTMLLinkElement::linkStyle() const 227 { 228 if (!m_link || m_link->type() != LinkResource::Style) 229 return 0; 230 return static_cast<LinkStyle*>(m_link.get()); 231 } 232 233 LinkImport* HTMLLinkElement::linkImport() const 234 { 235 if (!m_link || m_link->type() != LinkResource::Import) 236 return 0; 237 return static_cast<LinkImport*>(m_link.get()); 238 } 239 240 Document* HTMLLinkElement::import() const 241 { 242 if (LinkImport* link = linkImport()) 243 return link->importedDocument(); 244 return 0; 245 } 246 247 void HTMLLinkElement::process() 248 { 249 if (LinkResource* link = linkResourceToProcess()) 250 link->process(); 251 } 252 253 void HTMLLinkElement::enableIfExitTransitionStyle() 254 { 255 if (m_relAttribute.isTransitionExitingStylesheet()) { 256 if (LinkStyle* link = linkStyle()) 257 link->setDisabledState(false); 258 } 259 } 260 261 Node::InsertionNotificationRequest HTMLLinkElement::insertedInto(ContainerNode* insertionPoint) 262 { 263 HTMLElement::insertedInto(insertionPoint); 264 if (!insertionPoint->inDocument()) 265 return InsertionDone; 266 267 m_isInShadowTree = isInShadowTree(); 268 if (m_isInShadowTree) 269 return InsertionDone; 270 271 document().styleEngine()->addStyleSheetCandidateNode(this, m_createdByParser); 272 273 process(); 274 275 if (m_link) 276 m_link->ownerInserted(); 277 278 return InsertionDone; 279 } 280 281 void HTMLLinkElement::removedFrom(ContainerNode* insertionPoint) 282 { 283 HTMLElement::removedFrom(insertionPoint); 284 if (!insertionPoint->inDocument()) 285 return; 286 287 m_linkLoader.released(); 288 289 if (m_isInShadowTree) { 290 ASSERT(!linkStyle() || !linkStyle()->hasSheet()); 291 return; 292 } 293 document().styleEngine()->removeStyleSheetCandidateNode(this); 294 295 RefPtrWillBeRawPtr<StyleSheet> removedSheet = sheet(); 296 297 if (m_link) 298 m_link->ownerRemoved(); 299 300 document().removedStyleSheet(removedSheet.get()); 301 } 302 303 void HTMLLinkElement::finishParsingChildren() 304 { 305 m_createdByParser = false; 306 HTMLElement::finishParsingChildren(); 307 } 308 309 bool HTMLLinkElement::styleSheetIsLoading() const 310 { 311 return linkStyle() && linkStyle()->styleSheetIsLoading(); 312 } 313 314 void HTMLLinkElement::linkLoaded() 315 { 316 dispatchEvent(Event::create(EventTypeNames::load)); 317 } 318 319 void HTMLLinkElement::linkLoadingErrored() 320 { 321 dispatchEvent(Event::create(EventTypeNames::error)); 322 } 323 324 void HTMLLinkElement::didStartLinkPrerender() 325 { 326 dispatchEvent(Event::create(EventTypeNames::webkitprerenderstart)); 327 } 328 329 void HTMLLinkElement::didStopLinkPrerender() 330 { 331 dispatchEvent(Event::create(EventTypeNames::webkitprerenderstop)); 332 } 333 334 void HTMLLinkElement::didSendLoadForLinkPrerender() 335 { 336 dispatchEvent(Event::create(EventTypeNames::webkitprerenderload)); 337 } 338 339 void HTMLLinkElement::didSendDOMContentLoadedForLinkPrerender() 340 { 341 dispatchEvent(Event::create(EventTypeNames::webkitprerenderdomcontentloaded)); 342 } 343 344 bool HTMLLinkElement::sheetLoaded() 345 { 346 ASSERT(linkStyle()); 347 return linkStyle()->sheetLoaded(); 348 } 349 350 void HTMLLinkElement::notifyLoadedSheetAndAllCriticalSubresources(bool errorOccurred) 351 { 352 ASSERT(linkStyle()); 353 linkStyle()->notifyLoadedSheetAndAllCriticalSubresources(errorOccurred); 354 } 355 356 void HTMLLinkElement::dispatchPendingLoadEvents() 357 { 358 linkLoadEventSender().dispatchPendingEvents(); 359 } 360 361 void HTMLLinkElement::dispatchPendingEvent(LinkEventSender* eventSender) 362 { 363 ASSERT_UNUSED(eventSender, eventSender == &linkLoadEventSender()); 364 ASSERT(m_link); 365 if (m_link->hasLoaded()) 366 linkLoaded(); 367 else 368 linkLoadingErrored(); 369 } 370 371 void HTMLLinkElement::scheduleEvent() 372 { 373 linkLoadEventSender().dispatchEventSoon(this); 374 } 375 376 void HTMLLinkElement::startLoadingDynamicSheet() 377 { 378 ASSERT(linkStyle()); 379 linkStyle()->startLoadingDynamicSheet(); 380 } 381 382 bool HTMLLinkElement::isURLAttribute(const Attribute& attribute) const 383 { 384 return attribute.name().localName() == hrefAttr || HTMLElement::isURLAttribute(attribute); 385 } 386 387 bool HTMLLinkElement::hasLegalLinkAttribute(const QualifiedName& name) const 388 { 389 return name == hrefAttr || HTMLElement::hasLegalLinkAttribute(name); 390 } 391 392 const QualifiedName& HTMLLinkElement::subResourceAttributeName() const 393 { 394 // If the link element is not css, ignore it. 395 if (equalIgnoringCase(getAttribute(typeAttr), "text/css")) { 396 // FIXME: Add support for extracting links of sub-resources which 397 // are inside style-sheet such as @import, @font-face, url(), etc. 398 return hrefAttr; 399 } 400 return HTMLElement::subResourceAttributeName(); 401 } 402 403 KURL HTMLLinkElement::href() const 404 { 405 return document().completeURL(getAttribute(hrefAttr)); 406 } 407 408 const AtomicString& HTMLLinkElement::rel() const 409 { 410 return getAttribute(relAttr); 411 } 412 413 const AtomicString& HTMLLinkElement::type() const 414 { 415 return getAttribute(typeAttr); 416 } 417 418 bool HTMLLinkElement::async() const 419 { 420 return fastHasAttribute(HTMLNames::asyncAttr); 421 } 422 423 IconType HTMLLinkElement::iconType() const 424 { 425 return m_relAttribute.iconType(); 426 } 427 428 const Vector<IntSize>& HTMLLinkElement::iconSizes() const 429 { 430 return m_iconSizes; 431 } 432 433 DOMSettableTokenList* HTMLLinkElement::sizes() const 434 { 435 return m_sizes.get(); 436 } 437 438 void HTMLLinkElement::trace(Visitor* visitor) 439 { 440 visitor->trace(m_link); 441 visitor->trace(m_sizes); 442 HTMLElement::trace(visitor); 443 } 444 445 PassOwnPtrWillBeRawPtr<LinkStyle> LinkStyle::create(HTMLLinkElement* owner) 446 { 447 return adoptPtrWillBeNoop(new LinkStyle(owner)); 448 } 449 450 LinkStyle::LinkStyle(HTMLLinkElement* owner) 451 : LinkResource(owner) 452 , m_disabledState(Unset) 453 , m_pendingSheetType(None) 454 , m_loading(false) 455 , m_firedLoad(false) 456 , m_loadedSheet(false) 457 { 458 } 459 460 LinkStyle::~LinkStyle() 461 { 462 #if !ENABLE(OILPAN) 463 if (m_sheet) 464 m_sheet->clearOwnerNode(); 465 #endif 466 } 467 468 Document& LinkStyle::document() 469 { 470 return m_owner->document(); 471 } 472 473 void LinkStyle::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CSSStyleSheetResource* cachedStyleSheet) 474 { 475 if (!m_owner->inDocument()) { 476 ASSERT(!m_sheet); 477 return; 478 479 } 480 // Completing the sheet load may cause scripts to execute. 481 RefPtrWillBeRawPtr<Node> protector(m_owner.get()); 482 483 CSSParserContext parserContext(m_owner->document(), 0, baseURL, charset); 484 485 if (RefPtrWillBeRawPtr<StyleSheetContents> restoredSheet = const_cast<CSSStyleSheetResource*>(cachedStyleSheet)->restoreParsedStyleSheet(parserContext)) { 486 ASSERT(restoredSheet->isCacheable()); 487 ASSERT(!restoredSheet->isLoading()); 488 489 if (m_sheet) 490 clearSheet(); 491 m_sheet = CSSStyleSheet::create(restoredSheet, m_owner); 492 m_sheet->setMediaQueries(MediaQuerySet::create(m_owner->media())); 493 m_sheet->setTitle(m_owner->title()); 494 495 m_loading = false; 496 restoredSheet->checkLoaded(); 497 return; 498 } 499 500 RefPtrWillBeRawPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(href, parserContext); 501 502 if (m_sheet) 503 clearSheet(); 504 m_sheet = CSSStyleSheet::create(styleSheet, m_owner); 505 m_sheet->setMediaQueries(MediaQuerySet::create(m_owner->media())); 506 m_sheet->setTitle(m_owner->title()); 507 508 styleSheet->parseAuthorStyleSheet(cachedStyleSheet, m_owner->document().securityOrigin()); 509 510 m_loading = false; 511 styleSheet->notifyLoadedSheet(cachedStyleSheet); 512 styleSheet->checkLoaded(); 513 514 if (styleSheet->isCacheable()) 515 const_cast<CSSStyleSheetResource*>(cachedStyleSheet)->saveParsedStyleSheet(styleSheet); 516 } 517 518 bool LinkStyle::sheetLoaded() 519 { 520 if (!styleSheetIsLoading()) { 521 removePendingSheet(); 522 return true; 523 } 524 return false; 525 } 526 527 void LinkStyle::notifyLoadedSheetAndAllCriticalSubresources(bool errorOccurred) 528 { 529 if (m_firedLoad) 530 return; 531 m_loadedSheet = !errorOccurred; 532 if (m_owner) 533 m_owner->scheduleEvent(); 534 m_firedLoad = true; 535 } 536 537 void LinkStyle::startLoadingDynamicSheet() 538 { 539 ASSERT(m_pendingSheetType < Blocking); 540 addPendingSheet(Blocking); 541 } 542 543 void LinkStyle::clearSheet() 544 { 545 ASSERT(m_sheet); 546 ASSERT(m_sheet->ownerNode() == m_owner); 547 m_sheet->clearOwnerNode(); 548 m_sheet = nullptr; 549 } 550 551 bool LinkStyle::styleSheetIsLoading() const 552 { 553 if (m_loading) 554 return true; 555 if (!m_sheet) 556 return false; 557 return m_sheet->contents()->isLoading(); 558 } 559 560 void LinkStyle::addPendingSheet(PendingSheetType type) 561 { 562 if (type <= m_pendingSheetType) 563 return; 564 m_pendingSheetType = type; 565 566 if (m_pendingSheetType == NonBlocking) 567 return; 568 m_owner->document().styleEngine()->addPendingSheet(); 569 } 570 571 void LinkStyle::removePendingSheet() 572 { 573 PendingSheetType type = m_pendingSheetType; 574 m_pendingSheetType = None; 575 576 if (type == None) 577 return; 578 if (type == NonBlocking) { 579 // Tell StyleEngine to re-compute styleSheets of this m_owner's treescope. 580 m_owner->document().styleEngine()->modifiedStyleSheetCandidateNode(m_owner); 581 // Document::removePendingSheet() triggers the style selector recalc for blocking sheets. 582 // FIXME: We don't have enough knowledge at this point to know if we're adding or removing a sheet 583 // so we can't call addedStyleSheet() or removedStyleSheet(). 584 m_owner->document().styleResolverChanged(); 585 return; 586 } 587 588 m_owner->document().styleEngine()->removePendingSheet(m_owner); 589 } 590 591 void LinkStyle::setDisabledState(bool disabled) 592 { 593 LinkStyle::DisabledState oldDisabledState = m_disabledState; 594 m_disabledState = disabled ? Disabled : EnabledViaScript; 595 if (oldDisabledState != m_disabledState) { 596 // If we change the disabled state while the sheet is still loading, then we have to 597 // perform three checks: 598 if (styleSheetIsLoading()) { 599 // Check #1: The sheet becomes disabled while loading. 600 if (m_disabledState == Disabled) 601 removePendingSheet(); 602 603 // Check #2: An alternate sheet becomes enabled while it is still loading. 604 if (m_owner->relAttribute().isAlternate() && m_disabledState == EnabledViaScript) 605 addPendingSheet(Blocking); 606 607 // Check #3: A main sheet becomes enabled while it was still loading and 608 // after it was disabled via script. It takes really terrible code to make this 609 // happen (a double toggle for no reason essentially). This happens on 610 // virtualplastic.net, which manages to do about 12 enable/disables on only 3 611 // sheets. :) 612 if (!m_owner->relAttribute().isAlternate() && m_disabledState == EnabledViaScript && oldDisabledState == Disabled) 613 addPendingSheet(Blocking); 614 615 // If the sheet is already loading just bail. 616 return; 617 } 618 619 if (m_sheet) 620 m_sheet->setDisabled(disabled); 621 622 // Load the sheet, since it's never been loaded before. 623 if (!m_sheet && m_disabledState == EnabledViaScript) { 624 if (m_owner->shouldProcessStyle()) 625 process(); 626 } else { 627 // FIXME: We don't have enough knowledge here to know if we should call addedStyleSheet() or removedStyleSheet(). 628 m_owner->document().styleResolverChanged(); 629 } 630 } 631 } 632 633 void LinkStyle::process() 634 { 635 ASSERT(m_owner->shouldProcessStyle()); 636 String type = m_owner->typeValue().lower(); 637 LinkRequestBuilder builder(m_owner); 638 639 if (m_owner->relAttribute().iconType() != InvalidIcon && builder.url().isValid() && !builder.url().isEmpty()) { 640 if (!m_owner->shouldLoadLink()) 641 return; 642 if (!document().securityOrigin()->canDisplay(builder.url())) 643 return; 644 if (!document().contentSecurityPolicy()->allowImageFromSource(builder.url())) 645 return; 646 if (document().frame() && document().frame()->loader().client()) 647 document().frame()->loader().client()->dispatchDidChangeIcons(m_owner->relAttribute().iconType()); 648 } 649 650 if (!m_owner->loadLink(type, builder.url())) 651 return; 652 653 if ((m_disabledState != Disabled) && (m_owner->relAttribute().isStyleSheet() || m_owner->relAttribute().isTransitionExitingStylesheet()) 654 && shouldLoadResource() && builder.url().isValid()) { 655 656 if (resource()) { 657 removePendingSheet(); 658 clearResource(); 659 } 660 661 if (!m_owner->shouldLoadLink()) 662 return; 663 664 m_loading = true; 665 666 bool mediaQueryMatches = true; 667 if (!m_owner->media().isEmpty()) { 668 LocalFrame* frame = loadingFrame(); 669 if (Document* document = loadingFrame()->document()) { 670 RefPtr<RenderStyle> documentStyle = StyleResolver::styleForDocument(*document); 671 RefPtrWillBeRawPtr<MediaQuerySet> media = MediaQuerySet::create(m_owner->media()); 672 MediaQueryEvaluator evaluator(frame->view()->mediaType(), frame); 673 mediaQueryMatches = evaluator.eval(media.get()); 674 } 675 } 676 677 // Don't hold up render tree construction and script execution on stylesheets 678 // that are not needed for the rendering at the moment. 679 bool blocking = mediaQueryMatches && !m_owner->isAlternate(); 680 addPendingSheet(blocking ? Blocking : NonBlocking); 681 682 // Load stylesheets that are not needed for the rendering immediately with low priority. 683 FetchRequest request = builder.build(blocking); 684 AtomicString crossOriginMode = m_owner->fastGetAttribute(HTMLNames::crossoriginAttr); 685 if (!crossOriginMode.isNull()) 686 request.setCrossOriginAccessControl(document().securityOrigin(), crossOriginMode); 687 setResource(document().fetcher()->fetchCSSStyleSheet(request)); 688 689 if (!resource()) { 690 // The request may have been denied if (for example) the stylesheet is local and the document is remote. 691 m_loading = false; 692 removePendingSheet(); 693 } 694 } else if (m_sheet) { 695 // we no longer contain a stylesheet, e.g. perhaps rel or type was changed 696 RefPtrWillBeRawPtr<StyleSheet> removedSheet = m_sheet.get(); 697 clearSheet(); 698 document().removedStyleSheet(removedSheet.get()); 699 } 700 } 701 702 void LinkStyle::setSheetTitle(const String& title) 703 { 704 if (m_sheet) 705 m_sheet->setTitle(title); 706 } 707 708 void LinkStyle::ownerRemoved() 709 { 710 if (m_sheet) 711 clearSheet(); 712 713 if (styleSheetIsLoading()) 714 removePendingSheet(); 715 } 716 717 void LinkStyle::trace(Visitor* visitor) 718 { 719 visitor->trace(m_sheet); 720 LinkResource::trace(visitor); 721 } 722 723 } // namespace WebCore 724