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/events/Event.h"
     33 #include "core/events/EventSender.h"
     34 #include "core/dom/StyleEngine.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, (EventTypeNames::load));
     44     return sharedLoadEventSender;
     45 }
     46 
     47 inline HTMLStyleElement::HTMLStyleElement(Document& document, bool createdByParser)
     48     : HTMLElement(styleTag, document)
     49     , StyleElement(&document, createdByParser)
     50     , m_firedLoad(false)
     51     , m_loadedSheet(false)
     52     , m_scopedStyleRegistrationState(NotRegistered)
     53 {
     54     ScriptWrappable::init(this);
     55 }
     56 
     57 HTMLStyleElement::~HTMLStyleElement()
     58 {
     59     // During tear-down, willRemove isn't called, so m_scopedStyleRegistrationState may still be RegisteredAsScoped or RegisteredInShadowRoot here.
     60     // Therefore we can't ASSERT(m_scopedStyleRegistrationState == NotRegistered).
     61     StyleElement::clearDocumentData(document(), this);
     62 
     63     styleLoadEventSender().cancelEvent(this);
     64 }
     65 
     66 PassRefPtr<HTMLStyleElement> HTMLStyleElement::create(Document& document, bool createdByParser)
     67 {
     68     return adoptRef(new HTMLStyleElement(document, createdByParser));
     69 }
     70 
     71 void HTMLStyleElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
     72 {
     73     if (name == titleAttr && m_sheet) {
     74         m_sheet->setTitle(value);
     75     } else if (name == scopedAttr && ContextFeatures::styleScopedEnabled(&document())) {
     76         scopedAttributeChanged(!value.isNull());
     77     } else if (name == mediaAttr && inDocument() && document().isActive() && m_sheet) {
     78         m_sheet->setMediaQueries(MediaQuerySet::create(value));
     79         // FIXME: This shold be RecalcStyleDeferred.
     80         document().modifiedStyleSheet(m_sheet.get(), RecalcStyleImmediately);
     81     } else {
     82         HTMLElement::parseAttribute(name, value);
     83     }
     84 }
     85 
     86 void HTMLStyleElement::scopedAttributeChanged(bool scoped)
     87 {
     88     ASSERT(ContextFeatures::styleScopedEnabled(&document()));
     89 
     90     if (!inDocument())
     91         return;
     92 
     93     if (scoped) {
     94         if (m_scopedStyleRegistrationState == RegisteredAsScoped)
     95             return;
     96 
     97         // As any <style> in a shadow tree is treated as "scoped",
     98         // need to remove the <style> from its shadow root.
     99         ContainerNode* scopingNode = 0;
    100         if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) {
    101             scopingNode = containingShadowRoot();
    102             unregisterWithScopingNode(scopingNode);
    103         }
    104         document().styleEngine()->removeStyleSheetCandidateNode(this, scopingNode);
    105         registerWithScopingNode(true);
    106 
    107         document().styleEngine()->addStyleSheetCandidateNode(this, false);
    108         document().modifiedStyleSheet(sheet());
    109         return;
    110     }
    111 
    112     // If the <style> was scoped, need to remove the <style> from the scoping
    113     // element, i.e. the parent node.
    114     if (m_scopedStyleRegistrationState != RegisteredAsScoped)
    115         return;
    116 
    117     unregisterWithScopingNode(parentNode());
    118     document().styleEngine()->removeStyleSheetCandidateNode(this, parentNode());
    119 
    120     // As any <style> in a shadow tree is treated as "scoped",
    121     // need to add the <style> to its shadow root.
    122     if (isInShadowTree())
    123         registerWithScopingNode(false);
    124 
    125     document().styleEngine()->addStyleSheetCandidateNode(this, false);
    126     // FIXME: currently need to use FullStyleUpdate here.
    127     // Because ShadowTreeStyleSheetCollection doesn't know old scoping node.
    128     // So setNeedsStyleRecalc for old scoping node is not invoked.
    129     document().modifiedStyleSheet(sheet());
    130 }
    131 
    132 void HTMLStyleElement::finishParsingChildren()
    133 {
    134     StyleElement::finishParsingChildren(this);
    135     HTMLElement::finishParsingChildren();
    136 }
    137 
    138 void HTMLStyleElement::registerWithScopingNode(bool scoped)
    139 {
    140     // Note: We cannot rely on the 'scoped' element already being present when this method is invoked.
    141     // Therefore we cannot rely on scoped()!
    142     ASSERT(m_scopedStyleRegistrationState == NotRegistered);
    143     ASSERT(inDocument());
    144     if (m_scopedStyleRegistrationState != NotRegistered)
    145         return;
    146 
    147     ContainerNode* scope = scoped ? parentNode() : containingShadowRoot();
    148     if (!scope)
    149         return;
    150     if (!scope->isElementNode() && !scope->isShadowRoot()) {
    151         // DocumentFragment nodes should never be inDocument,
    152         // <style> should not be a child of Document, PI or some such.
    153         ASSERT_NOT_REACHED();
    154         return;
    155     }
    156     scope->registerScopedHTMLStyleChild();
    157     m_scopedStyleRegistrationState = scoped ? RegisteredAsScoped : RegisteredInShadowRoot;
    158 }
    159 
    160 void HTMLStyleElement::unregisterWithScopingNode(ContainerNode* scope)
    161 {
    162     ASSERT(m_scopedStyleRegistrationState != NotRegistered || !ContextFeatures::styleScopedEnabled(&document()));
    163     if (!isRegisteredAsScoped())
    164         return;
    165 
    166     ASSERT(scope);
    167     if (scope) {
    168         ASSERT(scope->hasScopedHTMLStyleChild());
    169         scope->unregisterScopedHTMLStyleChild();
    170     }
    171 
    172     m_scopedStyleRegistrationState = NotRegistered;
    173 }
    174 
    175 Node::InsertionNotificationRequest HTMLStyleElement::insertedInto(ContainerNode* insertionPoint)
    176 {
    177     HTMLElement::insertedInto(insertionPoint);
    178     if (insertionPoint->inDocument()) {
    179         if (m_scopedStyleRegistrationState == NotRegistered && (scoped() || isInShadowTree()))
    180             registerWithScopingNode(scoped());
    181     }
    182     return InsertionShouldCallDidNotifySubtreeInsertions;
    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::didNotifySubtreeInsertionsToDocument()
    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     dispatchEvent(Event::create(m_loadedSheet ? EventTypeNames::load : EventTypeNames::error));
    262 }
    263 
    264 void HTMLStyleElement::notifyLoadedSheetAndAllCriticalSubresources(bool errorOccurred)
    265 {
    266     if (m_firedLoad)
    267         return;
    268     m_loadedSheet = !errorOccurred;
    269     styleLoadEventSender().dispatchEventSoon(this);
    270     m_firedLoad = true;
    271 }
    272 
    273 void HTMLStyleElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
    274 {
    275     HTMLElement::addSubresourceAttributeURLs(urls);
    276 
    277     if (CSSStyleSheet* styleSheet = const_cast<HTMLStyleElement*>(this)->sheet())
    278         styleSheet->contents()->addSubresourceStyleURLs(urls);
    279 }
    280 
    281 bool HTMLStyleElement::disabled() const
    282 {
    283     if (!m_sheet)
    284         return false;
    285 
    286     return m_sheet->disabled();
    287 }
    288 
    289 void HTMLStyleElement::setDisabled(bool setDisabled)
    290 {
    291     if (CSSStyleSheet* styleSheet = sheet())
    292         styleSheet->setDisabled(setDisabled);
    293 }
    294 
    295 }
    296