1 /* 2 * (C) 1999-2003 Lars Knoll (knoll (at) kde.org) 3 * Copyright (C) 2004, 2006, 2007, 2012 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 "core/css/CSSStyleSheet.h" 23 24 #include "HTMLNames.h" 25 #include "SVGNames.h" 26 #include "bindings/v8/ExceptionState.h" 27 #include "core/css/CSSCharsetRule.h" 28 #include "core/css/CSSImportRule.h" 29 #include "core/css/CSSParser.h" 30 #include "core/css/CSSRuleList.h" 31 #include "core/css/CSSStyleRule.h" 32 #include "core/css/MediaList.h" 33 #include "core/css/StyleRule.h" 34 #include "core/css/StyleSheetContents.h" 35 #include "core/dom/Document.h" 36 #include "core/dom/ExceptionCode.h" 37 #include "core/dom/Node.h" 38 #include "core/frame/UseCounter.h" 39 #include "core/inspector/InspectorInstrumentation.h" 40 #include "platform/weborigin/SecurityOrigin.h" 41 #include "wtf/text/StringBuilder.h" 42 43 namespace WebCore { 44 45 class StyleSheetCSSRuleList : public CSSRuleList { 46 public: 47 StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { } 48 49 private: 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 62 static 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 || parentNode->hasTagName(SVGNames::styleTag) 70 || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE; 71 } 72 #endif 73 74 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, CSSImportRule* ownerRule) 75 { 76 return adoptRef(new CSSStyleSheet(sheet, ownerRule)); 77 } 78 79 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, Node* ownerNode) 80 { 81 return adoptRef(new CSSStyleSheet(sheet, ownerNode, false, TextPosition::minimumPosition())); 82 } 83 84 PassRefPtr<CSSStyleSheet> CSSStyleSheet::createInline(Node* ownerNode, const KURL& baseURL, const TextPosition& startPosition, const String& encoding) 85 { 86 CSSParserContext parserContext(ownerNode->document(), baseURL, encoding); 87 RefPtr<StyleSheetContents> sheet = StyleSheetContents::create(baseURL.string(), parserContext); 88 return adoptRef(new CSSStyleSheet(sheet.release(), ownerNode, true, startPosition)); 89 } 90 91 CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, CSSImportRule* ownerRule) 92 : m_contents(contents) 93 , m_isInlineStylesheet(false) 94 , m_isDisabled(false) 95 , m_ownerNode(0) 96 , m_ownerRule(ownerRule) 97 , m_startPosition(TextPosition::minimumPosition()) 98 { 99 m_contents->registerClient(this); 100 } 101 102 CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, Node* ownerNode, bool isInlineStylesheet, const TextPosition& startPosition) 103 : m_contents(contents) 104 , m_isInlineStylesheet(isInlineStylesheet) 105 , m_isDisabled(false) 106 , m_ownerNode(ownerNode) 107 , m_ownerRule(0) 108 , m_startPosition(startPosition) 109 { 110 ASSERT(isAcceptableCSSStyleSheetParent(ownerNode)); 111 m_contents->registerClient(this); 112 } 113 114 CSSStyleSheet::~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 124 for (unsigned i = 0; i < m_extraChildRuleCSSOMWrappers.size(); ++i) 125 m_extraChildRuleCSSOMWrappers[i]->setParentStyleSheet(0); 126 127 if (m_mediaCSSOMWrapper) 128 m_mediaCSSOMWrapper->clearParentStyleSheet(); 129 130 m_contents->unregisterClient(this); 131 } 132 133 void CSSStyleSheet::extraCSSOMWrapperIndices(Vector<unsigned>& indices) 134 { 135 indices.grow(m_extraChildRuleCSSOMWrappers.size()); 136 137 for (unsigned i = 0; i < m_extraChildRuleCSSOMWrappers.size(); ++i) { 138 CSSRule* cssRule = m_extraChildRuleCSSOMWrappers[i].get(); 139 ASSERT(cssRule->type() == CSSRule::STYLE_RULE); 140 StyleRule* styleRule = toCSSStyleRule(cssRule)->styleRule(); 141 142 bool didFindIndex = false; 143 for (unsigned j = 0; j < m_contents->ruleCount(); ++j) { 144 if (m_contents->ruleAt(j) == styleRule) { 145 didFindIndex = true; 146 indices[i] = j; 147 break; 148 } 149 } 150 ASSERT(didFindIndex); 151 if (!didFindIndex) 152 indices[i] = 0; 153 } 154 } 155 156 void CSSStyleSheet::willMutateRules() 157 { 158 InspectorInstrumentation::willMutateRules(this); 159 // If we are the only client it is safe to mutate. 160 if (m_contents->hasOneClient() && !m_contents->isInMemoryCache()) { 161 m_contents->clearRuleSet(); 162 m_contents->setMutable(); 163 return; 164 } 165 // Only cacheable stylesheets should have multiple clients. 166 ASSERT(m_contents->isCacheable()); 167 168 Vector<unsigned> indices; 169 extraCSSOMWrapperIndices(indices); 170 171 // Copy-on-write. 172 m_contents->unregisterClient(this); 173 m_contents = m_contents->copy(); 174 m_contents->registerClient(this); 175 176 m_contents->setMutable(); 177 178 // Any existing CSSOM wrappers need to be connected to the copied child rules. 179 reattachChildRuleCSSOMWrappers(indices); 180 } 181 182 void CSSStyleSheet::didMutateRules() 183 { 184 ASSERT(m_contents->isMutable()); 185 ASSERT(m_contents->hasOneClient()); 186 187 InspectorInstrumentation::didMutateRules(this); 188 didMutate(PartialRuleUpdate); 189 } 190 191 void CSSStyleSheet::didMutate(StyleSheetUpdateType updateType) 192 { 193 Document* owner = ownerDocument(); 194 if (!owner) 195 return; 196 197 // Need FullStyleUpdate when insertRule or deleteRule, 198 // because StyleSheetCollection::analyzeStyleSheetChange cannot detect partial rule update. 199 StyleResolverUpdateMode updateMode = updateType != PartialRuleUpdate ? AnalyzedStyleUpdate : FullStyleUpdate; 200 owner->modifiedStyleSheet(this, RecalcStyleDeferred, updateMode); 201 } 202 203 void CSSStyleSheet::registerExtraChildRuleCSSOMWrapper(PassRefPtr<CSSRule> rule) 204 { 205 m_extraChildRuleCSSOMWrappers.append(rule); 206 } 207 208 void CSSStyleSheet::reattachChildRuleCSSOMWrappers(const Vector<unsigned>& extraCSSOMWrapperIndices) 209 { 210 ASSERT(extraCSSOMWrapperIndices.size() == m_extraChildRuleCSSOMWrappers.size()); 211 for (unsigned i = 0; i < extraCSSOMWrapperIndices.size(); ++i) 212 m_extraChildRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(extraCSSOMWrapperIndices[i])); 213 214 for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) { 215 if (!m_childRuleCSSOMWrappers[i]) 216 continue; 217 m_childRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(i)); 218 } 219 } 220 221 void CSSStyleSheet::setDisabled(bool disabled) 222 { 223 if (disabled == m_isDisabled) 224 return; 225 m_isDisabled = disabled; 226 227 didMutate(); 228 } 229 230 void CSSStyleSheet::setMediaQueries(PassRefPtr<MediaQuerySet> mediaQueries) 231 { 232 m_mediaQueries = mediaQueries; 233 if (m_mediaCSSOMWrapper && m_mediaQueries) 234 m_mediaCSSOMWrapper->reattach(m_mediaQueries.get()); 235 236 // Add warning message to inspector whenever dpi/dpcm values are used for "screen" media. 237 reportMediaQueryWarningIfNeeded(ownerDocument(), m_mediaQueries.get()); 238 } 239 240 unsigned CSSStyleSheet::length() const 241 { 242 return m_contents->ruleCount(); 243 } 244 245 CSSRule* CSSStyleSheet::item(unsigned index) 246 { 247 unsigned ruleCount = length(); 248 if (index >= ruleCount) 249 return 0; 250 251 if (m_childRuleCSSOMWrappers.isEmpty()) 252 m_childRuleCSSOMWrappers.grow(ruleCount); 253 ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount); 254 255 RefPtr<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index]; 256 if (!cssRule) { 257 if (index == 0 && m_contents->hasCharsetRule()) { 258 ASSERT(!m_contents->ruleAt(0)); 259 cssRule = CSSCharsetRule::create(this, m_contents->encodingFromCharsetRule()); 260 } else 261 cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this); 262 } 263 return cssRule.get(); 264 } 265 266 bool CSSStyleSheet::canAccessRules() const 267 { 268 if (m_isInlineStylesheet) 269 return true; 270 KURL baseURL = m_contents->baseURL(); 271 if (baseURL.isEmpty()) 272 return true; 273 Document* document = ownerDocument(); 274 if (!document) 275 return true; 276 if (document->securityOrigin()->canRequest(baseURL)) 277 return true; 278 return false; 279 } 280 281 PassRefPtr<CSSRuleList> CSSStyleSheet::rules() 282 { 283 if (!canAccessRules()) 284 return 0; 285 // IE behavior. 286 RefPtr<StaticCSSRuleList> nonCharsetRules = StaticCSSRuleList::create(); 287 unsigned ruleCount = length(); 288 for (unsigned i = 0; i < ruleCount; ++i) { 289 CSSRule* rule = item(i); 290 if (rule->type() == CSSRule::CHARSET_RULE) 291 continue; 292 nonCharsetRules->rules().append(rule); 293 } 294 return nonCharsetRules.release(); 295 } 296 297 unsigned CSSStyleSheet::insertRule(const String& ruleString, unsigned index, ExceptionState& exceptionState) 298 { 299 ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount()); 300 301 if (index > length()) { 302 exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError); 303 return 0; 304 } 305 CSSParser p(m_contents->parserContext(), UseCounter::getFrom(this)); 306 RefPtr<StyleRuleBase> rule = p.parseRule(m_contents.get(), ruleString); 307 308 if (!rule) { 309 exceptionState.throwUninformativeAndGenericDOMException(SyntaxError); 310 return 0; 311 } 312 RuleMutationScope mutationScope(this); 313 314 bool success = m_contents->wrapperInsertRule(rule, index); 315 if (!success) { 316 exceptionState.throwUninformativeAndGenericDOMException(HierarchyRequestError); 317 return 0; 318 } 319 if (!m_childRuleCSSOMWrappers.isEmpty()) 320 m_childRuleCSSOMWrappers.insert(index, RefPtr<CSSRule>()); 321 322 return index; 323 } 324 325 unsigned CSSStyleSheet::insertRule(const String& rule, ExceptionState& exceptionState) 326 { 327 UseCounter::countDeprecation(activeExecutionContext(), UseCounter::CSSStyleSheetInsertRuleOptionalArg); 328 return insertRule(rule, 0, exceptionState); 329 } 330 331 void CSSStyleSheet::deleteRule(unsigned index, ExceptionState& exceptionState) 332 { 333 ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount()); 334 335 if (index >= length()) { 336 exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError); 337 return; 338 } 339 RuleMutationScope mutationScope(this); 340 341 m_contents->wrapperDeleteRule(index); 342 343 if (!m_childRuleCSSOMWrappers.isEmpty()) { 344 if (m_childRuleCSSOMWrappers[index]) 345 m_childRuleCSSOMWrappers[index]->setParentStyleSheet(0); 346 m_childRuleCSSOMWrappers.remove(index); 347 } 348 } 349 350 int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionState& exceptionState) 351 { 352 StringBuilder text; 353 text.append(selector); 354 text.appendLiteral(" { "); 355 text.append(style); 356 if (!style.isEmpty()) 357 text.append(' '); 358 text.append('}'); 359 insertRule(text.toString(), index, exceptionState); 360 361 // As per Microsoft documentation, always return -1. 362 return -1; 363 } 364 365 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionState& exceptionState) 366 { 367 return addRule(selector, style, length(), exceptionState); 368 } 369 370 371 PassRefPtr<CSSRuleList> CSSStyleSheet::cssRules() 372 { 373 if (!canAccessRules()) 374 return 0; 375 if (!m_ruleListCSSOMWrapper) 376 m_ruleListCSSOMWrapper = adoptPtr(new StyleSheetCSSRuleList(this)); 377 return m_ruleListCSSOMWrapper.get(); 378 } 379 380 String CSSStyleSheet::href() const 381 { 382 return m_contents->originalURL(); 383 } 384 385 KURL CSSStyleSheet::baseURL() const 386 { 387 return m_contents->baseURL(); 388 } 389 390 bool CSSStyleSheet::isLoading() const 391 { 392 return m_contents->isLoading(); 393 } 394 395 MediaList* CSSStyleSheet::media() const 396 { 397 if (!m_mediaQueries) 398 return 0; 399 400 if (!m_mediaCSSOMWrapper) 401 m_mediaCSSOMWrapper = MediaList::create(m_mediaQueries.get(), const_cast<CSSStyleSheet*>(this)); 402 return m_mediaCSSOMWrapper.get(); 403 } 404 405 CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const 406 { 407 return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0; 408 } 409 410 Document* CSSStyleSheet::ownerDocument() const 411 { 412 const CSSStyleSheet* root = this; 413 while (root->parentStyleSheet()) 414 root = root->parentStyleSheet(); 415 return root->ownerNode() ? &root->ownerNode()->document() : 0; 416 } 417 418 void CSSStyleSheet::clearChildRuleCSSOMWrappers() 419 { 420 m_childRuleCSSOMWrappers.clear(); 421 } 422 423 } 424