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