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, 2010 Apple Inc. All rights reserved. 6 * (C) 2007 Rob Buis (buis@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 "HTMLStyleElement.h" 26 27#include "Attribute.h" 28#include "ContextFeatures.h" 29#include "Document.h" 30#include "Event.h" 31#include "EventSender.h" 32#include "HTMLNames.h" 33#include "MediaList.h" 34#include "ScriptEventListener.h" 35#include "ScriptableDocumentParser.h" 36#include "ShadowRoot.h" 37#include "StyleSheetContents.h" 38 39namespace WebCore { 40 41using namespace HTMLNames; 42 43static StyleEventSender& styleLoadEventSender() 44{ 45 DEFINE_STATIC_LOCAL(StyleEventSender, sharedLoadEventSender, (eventNames().loadEvent)); 46 return sharedLoadEventSender; 47} 48 49inline HTMLStyleElement::HTMLStyleElement(const QualifiedName& tagName, Document* document, bool createdByParser) 50 : HTMLElement(tagName, document) 51 , StyleElement(document, createdByParser) 52 , m_firedLoad(false) 53 , m_loadedSheet(false) 54 , m_scopedStyleRegistrationState(NotRegistered) 55{ 56 ASSERT(hasTagName(styleTag)); 57} 58 59HTMLStyleElement::~HTMLStyleElement() 60{ 61 // During tear-down, willRemove isn't called, so m_scopedStyleRegistrationState may still be RegisteredAsScoped or RegisteredInShadowRoot here. 62 // Therefore we can't ASSERT(m_scopedStyleRegistrationState == NotRegistered). 63 StyleElement::clearDocumentData(document(), this); 64 65 styleLoadEventSender().cancelEvent(this); 66} 67 68PassRefPtr<HTMLStyleElement> HTMLStyleElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) 69{ 70 return adoptRef(new HTMLStyleElement(tagName, document, createdByParser)); 71} 72 73void HTMLStyleElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 74{ 75 if (name == titleAttr && m_sheet) 76 m_sheet->setTitle(value); 77 else if (name == scopedAttr && ContextFeatures::styleScopedEnabled(document())) 78 scopedAttributeChanged(!value.isNull()); 79 else if (name == mediaAttr && inDocument() && document()->renderer() && m_sheet) { 80 m_sheet->setMediaQueries(MediaQuerySet::createAllowingDescriptionSyntax(value)); 81 document()->styleResolverChanged(RecalcStyleImmediately); 82 } else 83 HTMLElement::parseAttribute(name, value); 84} 85 86void HTMLStyleElement::scopedAttributeChanged(bool scoped) 87{ 88 ASSERT(ContextFeatures::styleScopedEnabled(document())); 89 90 if (!inDocument()) 91 return; 92 93 if (scoped) { 94 // As any <style> in a shadow tree is treated as "scoped", 95 // need to remove the <style> from its shadow root. 96 if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) 97 unregisterWithScopingNode(containingShadowRoot()); 98 99 if (m_scopedStyleRegistrationState != RegisteredAsScoped) 100 registerWithScopingNode(true); 101 return; 102 } 103 104 // If the <style> was scoped, need to remove the <style> from the scoping 105 // element, i.e. the parent node. 106 if (m_scopedStyleRegistrationState == RegisteredAsScoped) 107 unregisterWithScopingNode(parentNode()); 108 109 // As any <style> in a shadow tree is treated as "scoped", 110 // need to add the <style> to its shadow root. 111 if (isInShadowTree() && m_scopedStyleRegistrationState != RegisteredInShadowRoot) 112 registerWithScopingNode(false); 113} 114 115void HTMLStyleElement::finishParsingChildren() 116{ 117 StyleElement::finishParsingChildren(this); 118 HTMLElement::finishParsingChildren(); 119} 120 121void HTMLStyleElement::registerWithScopingNode(bool scoped) 122{ 123 // Note: We cannot rely on the 'scoped' element already being present when this method is invoked. 124 // Therefore we cannot rely on scoped()! 125 ASSERT(m_scopedStyleRegistrationState == NotRegistered); 126 ASSERT(inDocument()); 127 if (m_scopedStyleRegistrationState != NotRegistered) 128 return; 129 130 ContainerNode* scope = scoped ? parentNode() : containingShadowRoot(); 131 if (!scope) 132 return; 133 if (!scope->isElementNode() && !scope->isShadowRoot()) { 134 // DocumentFragment nodes should never be inDocument, 135 // <style> should not be a child of Document, PI or some such. 136 ASSERT_NOT_REACHED(); 137 return; 138 } 139 scope->registerScopedHTMLStyleChild(); 140 if (scope->isShadowRoot()) 141 scope->shadowHost()->setNeedsStyleRecalc(); 142 else 143 scope->setNeedsStyleRecalc(); 144 if (inDocument() && !document()->parsing() && document()->renderer()) 145 document()->styleResolverChanged(DeferRecalcStyle); 146 147 m_scopedStyleRegistrationState = scoped ? RegisteredAsScoped : RegisteredInShadowRoot; 148} 149 150void HTMLStyleElement::unregisterWithScopingNode(ContainerNode* scope) 151{ 152 ASSERT(m_scopedStyleRegistrationState != NotRegistered || !ContextFeatures::styleScopedEnabled(document())); 153 if (!isRegisteredAsScoped()) 154 return; 155 156 ASSERT(scope); 157 if (scope) { 158 ASSERT(scope->hasScopedHTMLStyleChild()); 159 scope->unregisterScopedHTMLStyleChild(); 160 scope->setNeedsStyleRecalc(); 161 } 162 if (inDocument() && !document()->parsing() && document()->renderer()) 163 document()->styleResolverChanged(DeferRecalcStyle); 164 165 m_scopedStyleRegistrationState = NotRegistered; 166} 167 168Node::InsertionNotificationRequest HTMLStyleElement::insertedInto(ContainerNode* insertionPoint) 169{ 170 HTMLElement::insertedInto(insertionPoint); 171 if (insertionPoint->inDocument()) { 172 StyleElement::insertedIntoDocument(document(), this); 173 if (m_scopedStyleRegistrationState == NotRegistered && (scoped() || isInShadowTree())) 174 registerWithScopingNode(scoped()); 175 } 176 177 return InsertionDone; 178} 179 180void HTMLStyleElement::removedFrom(ContainerNode* insertionPoint) 181{ 182 HTMLElement::removedFrom(insertionPoint); 183 184 // In the current implementation, <style scoped> is only registered if the node is in the document. 185 // That is, because willRemove() is also called if an ancestor is removed from the document. 186 // Now, if we want to register <style scoped> even if it's not inDocument, 187 // we'd need to find a way to discern whether that is the case, or whether <style scoped> itself is about to be removed. 188 if (m_scopedStyleRegistrationState != NotRegistered) { 189 ContainerNode* scope; 190 if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) { 191 scope = containingShadowRoot(); 192 if (!scope) 193 scope = insertionPoint->containingShadowRoot(); 194 } else 195 scope = parentNode() ? parentNode() : insertionPoint; 196 unregisterWithScopingNode(scope); 197 } 198 199 if (insertionPoint->inDocument()) 200 StyleElement::removedFromDocument(document(), this); 201} 202 203void HTMLStyleElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) 204{ 205 HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 206 StyleElement::childrenChanged(this); 207} 208 209const AtomicString& HTMLStyleElement::media() const 210{ 211 return getAttribute(mediaAttr); 212} 213 214const AtomicString& HTMLStyleElement::type() const 215{ 216 return getAttribute(typeAttr); 217} 218 219bool HTMLStyleElement::scoped() const 220{ 221 return fastHasAttribute(scopedAttr) && ContextFeatures::styleScopedEnabled(document()); 222} 223 224void HTMLStyleElement::setScoped(bool scopedValue) 225{ 226 setBooleanAttribute(scopedAttr, scopedValue); 227} 228 229Element* HTMLStyleElement::scopingElement() const 230{ 231 if (!scoped()) 232 return 0; 233 234 // FIXME: This probably needs to be refined for scoped stylesheets within shadow DOM. 235 // As written, such a stylesheet could style the host element, as well as children of the host. 236 // OTOH, this paves the way for a :bound-element implementation. 237 ContainerNode* parentOrShadowHost = parentOrShadowHostNode(); 238 if (!parentOrShadowHost || !parentOrShadowHost->isElementNode()) 239 return 0; 240 241 return toElement(parentOrShadowHost); 242} 243 244void HTMLStyleElement::dispatchPendingLoadEvents() 245{ 246 styleLoadEventSender().dispatchPendingEvents(); 247} 248 249void HTMLStyleElement::dispatchPendingEvent(StyleEventSender* eventSender) 250{ 251 ASSERT_UNUSED(eventSender, eventSender == &styleLoadEventSender()); 252 if (m_loadedSheet) 253 dispatchEvent(Event::create(eventNames().loadEvent, false, false)); 254 else 255 dispatchEvent(Event::create(eventNames().errorEvent, false, false)); 256} 257 258void HTMLStyleElement::notifyLoadedSheetAndAllCriticalSubresources(bool errorOccurred) 259{ 260 if (m_firedLoad) 261 return; 262 m_loadedSheet = !errorOccurred; 263 styleLoadEventSender().dispatchEventSoon(this); 264 m_firedLoad = true; 265} 266 267void HTMLStyleElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 268{ 269 HTMLElement::addSubresourceAttributeURLs(urls); 270 271 if (CSSStyleSheet* styleSheet = const_cast<HTMLStyleElement*>(this)->sheet()) 272 styleSheet->contents()->addSubresourceStyleURLs(urls); 273} 274 275bool HTMLStyleElement::disabled() const 276{ 277 if (!m_sheet) 278 return false; 279 280 return m_sheet->disabled(); 281} 282 283void HTMLStyleElement::setDisabled(bool setDisabled) 284{ 285 if (CSSStyleSheet* styleSheet = sheet()) 286 styleSheet->setDisabled(setDisabled); 287} 288 289} 290