Home | History | Annotate | Download | only in html
      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, 2010 Apple Inc. All rights reserved.
      6  *           (C) 2007 Rob Buis (buis (at) kde.org)
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  */
     23 
     24 #include "config.h"
     25 #include "core/html/HTMLStyleElement.h"
     26 
     27 #include "HTMLNames.h"
     28 #include "core/css/MediaList.h"
     29 #include "core/css/StyleSheetContents.h"
     30 #include "core/dom/ContextFeatures.h"
     31 #include "core/dom/Document.h"
     32 #include "core/dom/DocumentStyleSheetCollection.h"
     33 #include "core/dom/Event.h"
     34 #include "core/dom/EventSender.h"
     35 #include "core/dom/shadow/ShadowRoot.h"
     36 
     37 namespace WebCore {
     38 
     39 using namespace HTMLNames;
     40 
     41 static StyleEventSender& styleLoadEventSender()
     42 {
     43     DEFINE_STATIC_LOCAL(StyleEventSender, sharedLoadEventSender, (eventNames().loadEvent));
     44     return sharedLoadEventSender;
     45 }
     46 
     47 inline HTMLStyleElement::HTMLStyleElement(const QualifiedName& tagName, Document* document, bool createdByParser)
     48     : HTMLElement(tagName, document)
     49     , StyleElement(document, createdByParser)
     50     , m_firedLoad(false)
     51     , m_loadedSheet(false)
     52     , m_scopedStyleRegistrationState(NotRegistered)
     53 {
     54     ASSERT(hasTagName(styleTag));
     55     ScriptWrappable::init(this);
     56 }
     57 
     58 HTMLStyleElement::~HTMLStyleElement()
     59 {
     60     // During tear-down, willRemove isn't called, so m_scopedStyleRegistrationState may still be RegisteredAsScoped or RegisteredInShadowRoot here.
     61     // Therefore we can't ASSERT(m_scopedStyleRegistrationState == NotRegistered).
     62     StyleElement::clearDocumentData(document(), this);
     63 
     64     styleLoadEventSender().cancelEvent(this);
     65 }
     66 
     67 PassRefPtr<HTMLStyleElement> HTMLStyleElement::create(const QualifiedName& tagName, Document* document, bool createdByParser)
     68 {
     69     return adoptRef(new HTMLStyleElement(tagName, document, createdByParser));
     70 }
     71 
     72 void HTMLStyleElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
     73 {
     74     if (name == titleAttr && m_sheet) {
     75         m_sheet->setTitle(value);
     76     } else if (name == scopedAttr && ContextFeatures::styleScopedEnabled(document())) {
     77         scopedAttributeChanged(!value.isNull());
     78     } else if (name == mediaAttr && inDocument() && document()->renderer() && m_sheet) {
     79         m_sheet->setMediaQueries(MediaQuerySet::create(value));
     80         // FIXME: This shold be DeferRecalcStyle.
     81         document()->modifiedStyleSheet(m_sheet.get(), RecalcStyleImmediately);
     82     } else {
     83         HTMLElement::parseAttribute(name, value);
     84     }
     85 }
     86 
     87 void HTMLStyleElement::scopedAttributeChanged(bool scoped)
     88 {
     89     ASSERT(ContextFeatures::styleScopedEnabled(document()));
     90 
     91     if (!inDocument())
     92         return;
     93 
     94     if (scoped) {
     95         if (m_scopedStyleRegistrationState == RegisteredAsScoped)
     96             return;
     97 
     98         // As any <style> in a shadow tree is treated as "scoped",
     99         // need to remove the <style> from its shadow root.
    100         ContainerNode* scopingNode = 0;
    101         if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) {
    102             scopingNode = containingShadowRoot();
    103             unregisterWithScopingNode(scopingNode);
    104         }
    105         document()->styleSheetCollection()->removeStyleSheetCandidateNode(this, scopingNode);
    106         registerWithScopingNode(true);
    107 
    108         document()->styleSheetCollection()->addStyleSheetCandidateNode(this, false);
    109         document()->modifiedStyleSheet(sheet());
    110         return;
    111     }
    112 
    113     // If the <style> was scoped, need to remove the <style> from the scoping
    114     // element, i.e. the parent node.
    115     if (m_scopedStyleRegistrationState != RegisteredAsScoped)
    116         return;
    117 
    118     document()->styleSheetCollection()->removeStyleSheetCandidateNode(this, parentNode());
    119     unregisterWithScopingNode(parentNode());
    120 
    121     // As any <style> in a shadow tree is treated as "scoped",
    122     // need to add the <style> to its shadow root.
    123     if (isInShadowTree())
    124         registerWithScopingNode(false);
    125 
    126     document()->styleSheetCollection()->addStyleSheetCandidateNode(this, false);
    127     document()->modifiedStyleSheet(sheet());
    128 }
    129 
    130 void HTMLStyleElement::finishParsingChildren()
    131 {
    132     StyleElement::finishParsingChildren(this);
    133     HTMLElement::finishParsingChildren();
    134 }
    135 
    136 void HTMLStyleElement::registerWithScopingNode(bool scoped)
    137 {
    138     // Note: We cannot rely on the 'scoped' element already being present when this method is invoked.
    139     // Therefore we cannot rely on scoped()!
    140     ASSERT(m_scopedStyleRegistrationState == NotRegistered);
    141     ASSERT(inDocument());
    142     if (m_scopedStyleRegistrationState != NotRegistered)
    143         return;
    144 
    145     ContainerNode* scope = scoped ? parentNode() : containingShadowRoot();
    146     if (!scope)
    147         return;
    148     if (!scope->isElementNode() && !scope->isShadowRoot()) {
    149         // DocumentFragment nodes should never be inDocument,
    150         // <style> should not be a child of Document, PI or some such.
    151         ASSERT_NOT_REACHED();
    152         return;
    153     }
    154     scope->registerScopedHTMLStyleChild();
    155     m_scopedStyleRegistrationState = scoped ? RegisteredAsScoped : RegisteredInShadowRoot;
    156 }
    157 
    158 void HTMLStyleElement::unregisterWithScopingNode(ContainerNode* scope)
    159 {
    160     ASSERT(m_scopedStyleRegistrationState != NotRegistered || !ContextFeatures::styleScopedEnabled(document()));
    161     if (!isRegisteredAsScoped())
    162         return;
    163 
    164     ASSERT(scope);
    165     if (scope) {
    166         ASSERT(scope->hasScopedHTMLStyleChild());
    167         scope->unregisterScopedHTMLStyleChild();
    168     }
    169 
    170     m_scopedStyleRegistrationState = NotRegistered;
    171 }
    172 
    173 Node::InsertionNotificationRequest HTMLStyleElement::insertedInto(ContainerNode* insertionPoint)
    174 {
    175     HTMLElement::insertedInto(insertionPoint);
    176     if (insertionPoint->inDocument()) {
    177         if (m_scopedStyleRegistrationState == NotRegistered && (scoped() || isInShadowTree()))
    178             registerWithScopingNode(scoped());
    179         return InsertionShouldCallDidNotifySubtreeInsertions;
    180     }
    181 
    182     return InsertionDone;
    183 }
    184 
    185 void HTMLStyleElement::removedFrom(ContainerNode* insertionPoint)
    186 {
    187     HTMLElement::removedFrom(insertionPoint);
    188 
    189     // In the current implementation, <style scoped> is only registered if the node is in the document.
    190     // That is, because willRemove() is also called if an ancestor is removed from the document.
    191     // Now, if we want to register <style scoped> even if it's not inDocument,
    192     // we'd need to find a way to discern whether that is the case, or whether <style scoped> itself is about to be removed.
    193     ContainerNode* scope = 0;
    194     if (m_scopedStyleRegistrationState != NotRegistered) {
    195         if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) {
    196             scope = containingShadowRoot();
    197             if (!scope)
    198                 scope = insertionPoint->containingShadowRoot();
    199         } else
    200             scope = parentNode() ? parentNode() : insertionPoint;
    201         unregisterWithScopingNode(scope);
    202     }
    203 
    204     if (insertionPoint->inDocument())
    205         StyleElement::removedFromDocument(document(), this, scope);
    206 }
    207 
    208 void HTMLStyleElement::didNotifySubtreeInsertions(ContainerNode* insertionPoint)
    209 {
    210     StyleElement::processStyleSheet(document(), this);
    211 }
    212 
    213 void HTMLStyleElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    214 {
    215     HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    216     StyleElement::childrenChanged(this);
    217 }
    218 
    219 const AtomicString& HTMLStyleElement::media() const
    220 {
    221     return getAttribute(mediaAttr);
    222 }
    223 
    224 const AtomicString& HTMLStyleElement::type() const
    225 {
    226     return getAttribute(typeAttr);
    227 }
    228 
    229 bool HTMLStyleElement::scoped() const
    230 {
    231     return fastHasAttribute(scopedAttr) && ContextFeatures::styleScopedEnabled(document());
    232 }
    233 
    234 void HTMLStyleElement::setScoped(bool scopedValue)
    235 {
    236     setBooleanAttribute(scopedAttr, scopedValue);
    237 }
    238 
    239 ContainerNode* HTMLStyleElement::scopingNode()
    240 {
    241     if (!inDocument())
    242         return 0;
    243 
    244     if (!isRegisteredAsScoped())
    245         return document();
    246 
    247     if (isRegisteredInShadowRoot())
    248         return containingShadowRoot();
    249 
    250     return parentNode();
    251 }
    252 
    253 void HTMLStyleElement::dispatchPendingLoadEvents()
    254 {
    255     styleLoadEventSender().dispatchPendingEvents();
    256 }
    257 
    258 void HTMLStyleElement::dispatchPendingEvent(StyleEventSender* eventSender)
    259 {
    260     ASSERT_UNUSED(eventSender, eventSender == &styleLoadEventSender());
    261     if (m_loadedSheet)
    262         dispatchEvent(Event::create(eventNames().loadEvent, false, false));
    263     else
    264         dispatchEvent(Event::create(eventNames().errorEvent, false, false));
    265 }
    266 
    267 void HTMLStyleElement::notifyLoadedSheetAndAllCriticalSubresources(bool errorOccurred)
    268 {
    269     if (m_firedLoad)
    270         return;
    271     m_loadedSheet = !errorOccurred;
    272     styleLoadEventSender().dispatchEventSoon(this);
    273     m_firedLoad = true;
    274 }
    275 
    276 void HTMLStyleElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
    277 {
    278     HTMLElement::addSubresourceAttributeURLs(urls);
    279 
    280     if (CSSStyleSheet* styleSheet = const_cast<HTMLStyleElement*>(this)->sheet())
    281         styleSheet->contents()->addSubresourceStyleURLs(urls);
    282 }
    283 
    284 bool HTMLStyleElement::disabled() const
    285 {
    286     if (!m_sheet)
    287         return false;
    288 
    289     return m_sheet->disabled();
    290 }
    291 
    292 void HTMLStyleElement::setDisabled(bool setDisabled)
    293 {
    294     if (CSSStyleSheet* styleSheet = sheet())
    295         styleSheet->setDisabled(setDisabled);
    296 }
    297 
    298 }
    299