• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@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:
StyleSheetCSSRuleList(CSSStyleSheet * sheet)47     StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { }
48 
49 private:
ref()50     virtual void ref() { m_styleSheet->ref(); }
deref()51     virtual void deref() { m_styleSheet->deref(); }
52 
length() const53     virtual unsigned length() const { return m_styleSheet->length(); }
item(unsigned index) const54     virtual CSSRule* item(unsigned index) const { return m_styleSheet->item(index); }
55 
styleSheet() const56     virtual CSSStyleSheet* styleSheet() const { return m_styleSheet; }
57 
58     CSSStyleSheet* m_styleSheet;
59 };
60 
61 #if !ASSERT_DISABLED
isAcceptableCSSStyleSheetParent(Node * parentNode)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 
create(PassRefPtr<StyleSheetContents> sheet,CSSImportRule * ownerRule)74 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, CSSImportRule* ownerRule)
75 {
76     return adoptRef(new CSSStyleSheet(sheet, ownerRule));
77 }
78 
create(PassRefPtr<StyleSheetContents> sheet,Node * ownerNode)79 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, Node* ownerNode)
80 {
81     return adoptRef(new CSSStyleSheet(sheet, ownerNode, false, TextPosition::minimumPosition()));
82 }
83 
createInline(Node * ownerNode,const KURL & baseURL,const TextPosition & startPosition,const String & encoding)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 
CSSStyleSheet(PassRefPtr<StyleSheetContents> contents,CSSImportRule * ownerRule)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 
CSSStyleSheet(PassRefPtr<StyleSheetContents> contents,Node * ownerNode,bool isInlineStylesheet,const TextPosition & startPosition)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 
~CSSStyleSheet()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 
extraCSSOMWrapperIndices(Vector<unsigned> & indices)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 
willMutateRules()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 
didMutateRules()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 
didMutate(StyleSheetUpdateType updateType)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 
registerExtraChildRuleCSSOMWrapper(PassRefPtr<CSSRule> rule)203 void CSSStyleSheet::registerExtraChildRuleCSSOMWrapper(PassRefPtr<CSSRule> rule)
204 {
205     m_extraChildRuleCSSOMWrappers.append(rule);
206 }
207 
reattachChildRuleCSSOMWrappers(const Vector<unsigned> & extraCSSOMWrapperIndices)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 
setDisabled(bool disabled)221 void CSSStyleSheet::setDisabled(bool disabled)
222 {
223     if (disabled == m_isDisabled)
224         return;
225     m_isDisabled = disabled;
226 
227     didMutate();
228 }
229 
setMediaQueries(PassRefPtr<MediaQuerySet> mediaQueries)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 
length() const240 unsigned CSSStyleSheet::length() const
241 {
242     return m_contents->ruleCount();
243 }
244 
item(unsigned index)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 
canAccessRules() const266 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 
rules()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 
insertRule(const String & ruleString,unsigned index,ExceptionState & exceptionState)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 
insertRule(const String & rule,ExceptionState & exceptionState)325 unsigned CSSStyleSheet::insertRule(const String& rule, ExceptionState& exceptionState)
326 {
327     UseCounter::countDeprecation(activeExecutionContext(), UseCounter::CSSStyleSheetInsertRuleOptionalArg);
328     return insertRule(rule, 0, exceptionState);
329 }
330 
deleteRule(unsigned index,ExceptionState & exceptionState)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 
addRule(const String & selector,const String & style,int index,ExceptionState & exceptionState)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 
addRule(const String & selector,const String & style,ExceptionState & exceptionState)365 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionState& exceptionState)
366 {
367     return addRule(selector, style, length(), exceptionState);
368 }
369 
370 
cssRules()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 
href() const380 String CSSStyleSheet::href() const
381 {
382     return m_contents->originalURL();
383 }
384 
baseURL() const385 KURL CSSStyleSheet::baseURL() const
386 {
387     return m_contents->baseURL();
388 }
389 
isLoading() const390 bool CSSStyleSheet::isLoading() const
391 {
392     return m_contents->isLoading();
393 }
394 
media() const395 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 
parentStyleSheet() const405 CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const
406 {
407     return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
408 }
409 
ownerDocument() const410 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 
clearChildRuleCSSOMWrappers()418 void CSSStyleSheet::clearChildRuleCSSOMWrappers()
419 {
420     m_childRuleCSSOMWrappers.clear();
421 }
422 
423 }
424