1/* 2 * (C) 1999-2003 Lars Knoll (knoll@kde.org) 3 * Copyright (C) 2004, 2006, 2007, 2012, 2013 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21#include "config.h" 22#include "CSSStyleSheet.h" 23 24#include "CSSCharsetRule.h" 25#include "CSSFontFaceRule.h" 26#include "CSSImportRule.h" 27#include "CSSParser.h" 28#include "CSSRuleList.h" 29#include "CSSStyleRule.h" 30#include "CachedCSSStyleSheet.h" 31#include "Document.h" 32#include "DocumentStyleSheetCollection.h" 33#include "ExceptionCode.h" 34#include "HTMLNames.h" 35#include "MediaList.h" 36#include "Node.h" 37#include "SVGNames.h" 38#include "SecurityOrigin.h" 39#include "StyleRule.h" 40#include "StyleSheetContents.h" 41#include <wtf/text/StringBuilder.h> 42 43namespace WebCore { 44 45class StyleSheetCSSRuleList : public CSSRuleList { 46public: 47 StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { } 48 49private: 50 virtual void ref() { m_styleSheet->ref(); } 51 virtual void deref() { m_styleSheet->deref(); } 52 53 virtual unsigned length() const { return m_styleSheet->length(); } 54 virtual CSSRule* item(unsigned index) const { return m_styleSheet->item(index); } 55 56 virtual CSSStyleSheet* styleSheet() const { return m_styleSheet; } 57 58 CSSStyleSheet* m_styleSheet; 59}; 60 61#if !ASSERT_DISABLED 62static bool isAcceptableCSSStyleSheetParent(Node* parentNode) 63{ 64 // Only these nodes can be parents of StyleSheets, and they need to call clearOwnerNode() when moved out of document. 65 return !parentNode 66 || parentNode->isDocumentNode() 67 || parentNode->hasTagName(HTMLNames::linkTag) 68 || parentNode->hasTagName(HTMLNames::styleTag) 69#if ENABLE(SVG) 70 || parentNode->hasTagName(SVGNames::styleTag) 71#endif 72 || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE; 73} 74#endif 75 76PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, CSSImportRule* ownerRule) 77{ 78 return adoptRef(new CSSStyleSheet(sheet, ownerRule)); 79} 80 81PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, Node* ownerNode) 82{ 83 return adoptRef(new CSSStyleSheet(sheet, ownerNode, false)); 84} 85 86PassRefPtr<CSSStyleSheet> CSSStyleSheet::createInline(Node* ownerNode, const KURL& baseURL, const String& encoding) 87{ 88 CSSParserContext parserContext(ownerNode->document(), baseURL, encoding); 89 RefPtr<StyleSheetContents> sheet = StyleSheetContents::create(baseURL.string(), parserContext); 90 return adoptRef(new CSSStyleSheet(sheet.release(), ownerNode, true)); 91} 92 93CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, CSSImportRule* ownerRule) 94 : m_contents(contents) 95 , m_isInlineStylesheet(false) 96 , m_isDisabled(false) 97 , m_ownerNode(0) 98 , m_ownerRule(ownerRule) 99{ 100 m_contents->registerClient(this); 101} 102 103CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, Node* ownerNode, bool isInlineStylesheet) 104 : m_contents(contents) 105 , m_isInlineStylesheet(isInlineStylesheet) 106 , m_isDisabled(false) 107 , m_ownerNode(ownerNode) 108 , m_ownerRule(0) 109{ 110 ASSERT(isAcceptableCSSStyleSheetParent(ownerNode)); 111 m_contents->registerClient(this); 112} 113 114CSSStyleSheet::~CSSStyleSheet() 115{ 116 // For style rules outside the document, .parentStyleSheet can become null even if the style rule 117 // is still observable from JavaScript. This matches the behavior of .parentNode for nodes, but 118 // it's not ideal because it makes the CSSOM's behavior depend on the timing of garbage collection. 119 for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) { 120 if (m_childRuleCSSOMWrappers[i]) 121 m_childRuleCSSOMWrappers[i]->setParentStyleSheet(0); 122 } 123 if (m_mediaCSSOMWrapper) 124 m_mediaCSSOMWrapper->clearParentStyleSheet(); 125 126 m_contents->unregisterClient(this); 127} 128 129CSSStyleSheet::WhetherContentsWereClonedForMutation CSSStyleSheet::willMutateRules() 130{ 131 // If we are the only client it is safe to mutate. 132 if (m_contents->hasOneClient() && !m_contents->isInMemoryCache()) { 133 m_contents->setMutable(); 134 return ContentsWereNotClonedForMutation; 135 } 136 // Only cacheable stylesheets should have multiple clients. 137 ASSERT(m_contents->isCacheable()); 138 139 // Copy-on-write. 140 m_contents->unregisterClient(this); 141 m_contents = m_contents->copy(); 142 m_contents->registerClient(this); 143 144 m_contents->setMutable(); 145 146 // Any existing CSSOM wrappers need to be connected to the copied child rules. 147 reattachChildRuleCSSOMWrappers(); 148 149 return ContentsWereClonedForMutation; 150} 151 152void CSSStyleSheet::didMutateRuleFromCSSStyleDeclaration() 153{ 154 ASSERT(m_contents->isMutable()); 155 ASSERT(m_contents->hasOneClient()); 156 didMutate(); 157} 158 159void CSSStyleSheet::didMutateRules(RuleMutationType mutationType, WhetherContentsWereClonedForMutation contentsWereClonedForMutation) 160{ 161 ASSERT(m_contents->isMutable()); 162 ASSERT(m_contents->hasOneClient()); 163 164 Document* owner = ownerDocument(); 165 if (!owner) 166 return; 167 168 if (mutationType == RuleInsertion && !contentsWereClonedForMutation && !owner->styleSheetCollection()->activeStyleSheetsContains(this)) { 169 owner->scheduleOptimizedStyleSheetUpdate(); 170 return; 171 } 172 173 owner->styleResolverChanged(DeferRecalcStyle); 174} 175 176void CSSStyleSheet::didMutate() 177{ 178 Document* owner = ownerDocument(); 179 if (!owner) 180 return; 181 owner->styleResolverChanged(DeferRecalcStyle); 182} 183 184void CSSStyleSheet::clearOwnerNode() 185{ 186 Document* owner = ownerDocument(); 187 m_ownerNode = 0; 188 if (!owner) 189 return; 190 owner->styleResolverChanged(DeferRecalcStyleIfNeeded); 191} 192 193void CSSStyleSheet::reattachChildRuleCSSOMWrappers() 194{ 195 for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) { 196 if (!m_childRuleCSSOMWrappers[i]) 197 continue; 198 m_childRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(i)); 199 } 200} 201 202void CSSStyleSheet::setDisabled(bool disabled) 203{ 204 if (disabled == m_isDisabled) 205 return; 206 m_isDisabled = disabled; 207 208 didMutate(); 209} 210 211void CSSStyleSheet::setMediaQueries(PassRefPtr<MediaQuerySet> mediaQueries) 212{ 213 m_mediaQueries = mediaQueries; 214 if (m_mediaCSSOMWrapper && m_mediaQueries) 215 m_mediaCSSOMWrapper->reattach(m_mediaQueries.get()); 216 217#if ENABLE(RESOLUTION_MEDIA_QUERY) 218 // Add warning message to inspector whenever dpi/dpcm values are used for "screen" media. 219 reportMediaQueryWarningIfNeeded(ownerDocument(), m_mediaQueries.get()); 220#endif 221} 222 223unsigned CSSStyleSheet::length() const 224{ 225 return m_contents->ruleCount(); 226} 227 228CSSRule* CSSStyleSheet::item(unsigned index) 229{ 230 unsigned ruleCount = length(); 231 if (index >= ruleCount) 232 return 0; 233 234 if (m_childRuleCSSOMWrappers.isEmpty()) 235 m_childRuleCSSOMWrappers.grow(ruleCount); 236 ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount); 237 238 RefPtr<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index]; 239 if (!cssRule) { 240 if (index == 0 && m_contents->hasCharsetRule()) { 241 ASSERT(!m_contents->ruleAt(0)); 242 cssRule = CSSCharsetRule::create(this, m_contents->encodingFromCharsetRule()); 243 } else 244 cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this); 245 } 246 return cssRule.get(); 247} 248 249bool CSSStyleSheet::canAccessRules() const 250{ 251 if (m_isInlineStylesheet) 252 return true; 253 KURL baseURL = m_contents->baseURL(); 254 if (baseURL.isEmpty()) 255 return true; 256 Document* document = ownerDocument(); 257 if (!document) 258 return true; 259 if (document->securityOrigin()->canRequest(baseURL)) 260 return true; 261 return false; 262} 263 264PassRefPtr<CSSRuleList> CSSStyleSheet::rules() 265{ 266 if (!canAccessRules()) 267 return 0; 268 // IE behavior. 269 RefPtr<StaticCSSRuleList> nonCharsetRules = StaticCSSRuleList::create(); 270 unsigned ruleCount = length(); 271 for (unsigned i = 0; i < ruleCount; ++i) { 272 CSSRule* rule = item(i); 273 if (rule->type() == CSSRule::CHARSET_RULE) 274 continue; 275 nonCharsetRules->rules().append(rule); 276 } 277 return nonCharsetRules.release(); 278} 279 280unsigned CSSStyleSheet::insertRule(const String& ruleString, unsigned index, ExceptionCode& ec) 281{ 282 ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount()); 283 284 ec = 0; 285 if (index > length()) { 286 ec = INDEX_SIZE_ERR; 287 return 0; 288 } 289 CSSParser p(m_contents->parserContext()); 290 RefPtr<StyleRuleBase> rule = p.parseRule(m_contents.get(), ruleString); 291 292 if (!rule) { 293 ec = SYNTAX_ERR; 294 return 0; 295 } 296 297 RuleMutationScope mutationScope(this, RuleInsertion); 298 299 bool success = m_contents->wrapperInsertRule(rule, index); 300 if (!success) { 301 ec = HIERARCHY_REQUEST_ERR; 302 return 0; 303 } 304 if (!m_childRuleCSSOMWrappers.isEmpty()) 305 m_childRuleCSSOMWrappers.insert(index, RefPtr<CSSRule>()); 306 307 return index; 308} 309 310void CSSStyleSheet::deleteRule(unsigned index, ExceptionCode& ec) 311{ 312 ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount()); 313 314 ec = 0; 315 if (index >= length()) { 316 ec = INDEX_SIZE_ERR; 317 return; 318 } 319 RuleMutationScope mutationScope(this); 320 321 m_contents->wrapperDeleteRule(index); 322 323 if (!m_childRuleCSSOMWrappers.isEmpty()) { 324 if (m_childRuleCSSOMWrappers[index]) 325 m_childRuleCSSOMWrappers[index]->setParentStyleSheet(0); 326 m_childRuleCSSOMWrappers.remove(index); 327 } 328} 329 330int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionCode& ec) 331{ 332 StringBuilder text; 333 text.append(selector); 334 text.appendLiteral(" { "); 335 text.append(style); 336 if (!style.isEmpty()) 337 text.append(' '); 338 text.append('}'); 339 insertRule(text.toString(), index, ec); 340 341 // As per Microsoft documentation, always return -1. 342 return -1; 343} 344 345int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionCode& ec) 346{ 347 return addRule(selector, style, length(), ec); 348} 349 350 351PassRefPtr<CSSRuleList> CSSStyleSheet::cssRules() 352{ 353 if (!canAccessRules()) 354 return 0; 355 if (!m_ruleListCSSOMWrapper) 356 m_ruleListCSSOMWrapper = adoptPtr(new StyleSheetCSSRuleList(this)); 357 return m_ruleListCSSOMWrapper.get(); 358} 359 360String CSSStyleSheet::href() const 361{ 362 return m_contents->originalURL(); 363} 364 365KURL CSSStyleSheet::baseURL() const 366{ 367 return m_contents->baseURL(); 368} 369 370bool CSSStyleSheet::isLoading() const 371{ 372 return m_contents->isLoading(); 373} 374 375MediaList* CSSStyleSheet::media() const 376{ 377 if (!m_mediaQueries) 378 return 0; 379 380 if (!m_mediaCSSOMWrapper) 381 m_mediaCSSOMWrapper = MediaList::create(m_mediaQueries.get(), const_cast<CSSStyleSheet*>(this)); 382 return m_mediaCSSOMWrapper.get(); 383} 384 385CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const 386{ 387 return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0; 388} 389 390Document* CSSStyleSheet::ownerDocument() const 391{ 392 const CSSStyleSheet* root = this; 393 while (root->parentStyleSheet()) 394 root = root->parentStyleSheet(); 395 return root->ownerNode() ? root->ownerNode()->document() : 0; 396} 397 398void CSSStyleSheet::clearChildRuleCSSOMWrappers() 399{ 400 m_childRuleCSSOMWrappers.clear(); 401} 402 403CSSStyleSheet::RuleMutationScope::RuleMutationScope(CSSStyleSheet* sheet, RuleMutationType mutationType) 404 : m_styleSheet(sheet) 405 , m_mutationType(mutationType) 406{ 407 ASSERT(m_styleSheet); 408 m_contentsWereClonedForMutation = m_styleSheet->willMutateRules(); 409} 410 411CSSStyleSheet::RuleMutationScope::RuleMutationScope(CSSRule* rule) 412 : m_styleSheet(rule ? rule->parentStyleSheet() : 0) 413 , m_mutationType(OtherMutation) 414 , m_contentsWereClonedForMutation(ContentsWereNotClonedForMutation) 415{ 416 if (m_styleSheet) 417 m_contentsWereClonedForMutation = m_styleSheet->willMutateRules(); 418} 419 420CSSStyleSheet::RuleMutationScope::~RuleMutationScope() 421{ 422 if (m_styleSheet) 423 m_styleSheet->didMutateRules(m_mutationType, m_contentsWereClonedForMutation); 424} 425 426} 427