Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / CSSStyleSheet.cpp
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 "bindings/v8/V8PerIsolateData.h"
28 #include "core/css/CSSCharsetRule.h"
29 #include "core/css/CSSImportRule.h"
30 #include "core/css/parser/BisonCSSParser.h"
31 #include "core/css/CSSRuleList.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/ContentSecurityPolicy.h"
39 #include "core/frame/UseCounter.h"
40 #include "core/inspector/InspectorInstrumentation.h"
41 #include "platform/weborigin/SecurityOrigin.h"
42 #include "wtf/text/StringBuilder.h"
43
44 namespace WebCore {
45
46 class StyleSheetCSSRuleList FINAL : public CSSRuleList {
47 public:
48     StyleSheetCSSRuleList(CSSStyleSheet* sheet) : m_styleSheet(sheet) { }
49
50 private:
51     virtual void ref() OVERRIDE { m_styleSheet->ref(); }
52     virtual void deref() OVERRIDE { m_styleSheet->deref(); }
53
54     virtual unsigned length() const OVERRIDE { return m_styleSheet->length(); }
55     virtual CSSRule* item(unsigned index) const OVERRIDE { return m_styleSheet->item(index); }
56
57     virtual CSSStyleSheet* styleSheet() const OVERRIDE { return m_styleSheet; }
58
59     CSSStyleSheet* m_styleSheet;
60 };
61
62 #if !ASSERT_DISABLED
63 static bool isAcceptableCSSStyleSheetParent(Node* parentNode)
64 {
65     // Only these nodes can be parents of StyleSheets, and they need to call clearOwnerNode() when moved out of document.
66     return !parentNode
67         || parentNode->isDocumentNode()
68         || parentNode->hasTagName(HTMLNames::linkTag)
69         || parentNode->hasTagName(HTMLNames::styleTag)
70         || parentNode->hasTagName(SVGNames::styleTag)
71         || parentNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE;
72 }
73 #endif
74
75 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, CSSImportRule* ownerRule)
76 {
77     return adoptRef(new CSSStyleSheet(sheet, ownerRule));
78 }
79
80 PassRefPtr<CSSStyleSheet> CSSStyleSheet::create(PassRefPtr<StyleSheetContents> sheet, Node* ownerNode)
81 {
82     return adoptRef(new CSSStyleSheet(sheet, ownerNode, false, TextPosition::minimumPosition()));
83 }
84
85 PassRefPtr<CSSStyleSheet> CSSStyleSheet::createInline(PassRefPtr<StyleSheetContents> sheet, Node* ownerNode, const TextPosition& startPosition)
86 {
87     ASSERT(sheet);
88     return adoptRef(new CSSStyleSheet(sheet, ownerNode, true, startPosition));
89 }
90
91 PassRefPtr<CSSStyleSheet> CSSStyleSheet::createInline(Node* ownerNode, const KURL& baseURL, const TextPosition& startPosition, const String& encoding)
92 {
93     CSSParserContext parserContext(ownerNode->document(), 0, baseURL, encoding);
94     RefPtr<StyleSheetContents> sheet = StyleSheetContents::create(baseURL.string(), parserContext);
95     return adoptRef(new CSSStyleSheet(sheet.release(), ownerNode, true, startPosition));
96 }
97
98 CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, CSSImportRule* ownerRule)
99     : m_contents(contents)
100     , m_isInlineStylesheet(false)
101     , m_isDisabled(false)
102     , m_ownerNode(0)
103     , m_ownerRule(ownerRule)
104     , m_startPosition(TextPosition::minimumPosition())
105     , m_loadCompleted(false)
106 {
107     m_contents->registerClient(this);
108 }
109
110 CSSStyleSheet::CSSStyleSheet(PassRefPtr<StyleSheetContents> contents, Node* ownerNode, bool isInlineStylesheet, const TextPosition& startPosition)
111     : m_contents(contents)
112     , m_isInlineStylesheet(isInlineStylesheet)
113     , m_isDisabled(false)
114     , m_ownerNode(ownerNode)
115     , m_ownerRule(0)
116     , m_startPosition(startPosition)
117     , m_loadCompleted(false)
118 {
119     ASSERT(isAcceptableCSSStyleSheetParent(ownerNode));
120     m_contents->registerClient(this);
121 }
122
123 CSSStyleSheet::~CSSStyleSheet()
124 {
125     // For style rules outside the document, .parentStyleSheet can become null even if the style rule
126     // is still observable from JavaScript. This matches the behavior of .parentNode for nodes, but
127     // it's not ideal because it makes the CSSOM's behavior depend on the timing of garbage collection.
128     for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
129         if (m_childRuleCSSOMWrappers[i])
130             m_childRuleCSSOMWrappers[i]->setParentStyleSheet(0);
131     }
132
133     if (m_mediaCSSOMWrapper)
134         m_mediaCSSOMWrapper->clearParentStyleSheet();
135
136     m_contents->unregisterClient(this);
137 }
138
139 void CSSStyleSheet::willMutateRules()
140 {
141     InspectorInstrumentation::willMutateRules(this);
142     // If we are the only client it is safe to mutate.
143     if (m_contents->hasOneClient() && !m_contents->isInMemoryCache()) {
144         m_contents->clearRuleSet();
145         if (m_contents->maybeCacheable())
146             StyleEngine::removeSheet(m_contents.get());
147         m_contents->setMutable();
148         return;
149     }
150     // Only cacheable stylesheets should have multiple clients.
151     ASSERT(m_contents->isCacheable());
152
153     // Copy-on-write.
154     m_contents->unregisterClient(this);
155     m_contents = m_contents->copy();
156     m_contents->registerClient(this);
157
158     m_contents->setMutable();
159
160     // Any existing CSSOM wrappers need to be connected to the copied child rules.
161     reattachChildRuleCSSOMWrappers();
162 }
163
164 void CSSStyleSheet::didMutateRules()
165 {
166     ASSERT(m_contents->isMutable());
167     ASSERT(m_contents->hasOneClient());
168
169     InspectorInstrumentation::didMutateRules(this);
170     didMutate(PartialRuleUpdate);
171 }
172
173 void CSSStyleSheet::didMutate(StyleSheetUpdateType updateType)
174 {
175     Document* owner = ownerDocument();
176     if (!owner)
177         return;
178
179     // Need FullStyleUpdate when insertRule or deleteRule,
180     // because StyleSheetCollection::analyzeStyleSheetChange cannot detect partial rule update.
181     StyleResolverUpdateMode updateMode = updateType != PartialRuleUpdate ? AnalyzedStyleUpdate : FullStyleUpdate;
182     owner->modifiedStyleSheet(this, RecalcStyleDeferred, updateMode);
183 }
184
185 void CSSStyleSheet::reattachChildRuleCSSOMWrappers()
186 {
187     for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
188         if (!m_childRuleCSSOMWrappers[i])
189             continue;
190         m_childRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(i));
191     }
192 }
193
194 void CSSStyleSheet::setDisabled(bool disabled)
195 {
196     if (disabled == m_isDisabled)
197         return;
198     m_isDisabled = disabled;
199
200     didMutate();
201 }
202
203 void CSSStyleSheet::setMediaQueries(PassRefPtr<MediaQuerySet> mediaQueries)
204 {
205     m_mediaQueries = mediaQueries;
206     if (m_mediaCSSOMWrapper && m_mediaQueries)
207         m_mediaCSSOMWrapper->reattach(m_mediaQueries.get());
208
209     // Add warning message to inspector whenever dpi/dpcm values are used for "screen" media.
210     reportMediaQueryWarningIfNeeded(ownerDocument(), m_mediaQueries.get());
211 }
212
213 unsigned CSSStyleSheet::length() const
214 {
215     return m_contents->ruleCount();
216 }
217
218 CSSRule* CSSStyleSheet::item(unsigned index)
219 {
220     unsigned ruleCount = length();
221     if (index >= ruleCount)
222         return 0;
223
224     if (m_childRuleCSSOMWrappers.isEmpty())
225         m_childRuleCSSOMWrappers.grow(ruleCount);
226     ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount);
227
228     RefPtr<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index];
229     if (!cssRule) {
230         if (index == 0 && m_contents->hasCharsetRule()) {
231             ASSERT(!m_contents->ruleAt(0));
232             cssRule = CSSCharsetRule::create(this, m_contents->encodingFromCharsetRule());
233         } else
234             cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this);
235     }
236     return cssRule.get();
237 }
238
239 bool CSSStyleSheet::canAccessRules() const
240 {
241     if (m_isInlineStylesheet)
242         return true;
243     KURL baseURL = m_contents->baseURL();
244     if (baseURL.isEmpty())
245         return true;
246     Document* document = ownerDocument();
247     if (!document)
248         return true;
249     if (document->securityOrigin()->canRequest(baseURL))
250         return true;
251     return false;
252 }
253
254 PassRefPtr<CSSRuleList> CSSStyleSheet::rules()
255 {
256     if (!canAccessRules())
257         return 0;
258     // IE behavior.
259     RefPtr<StaticCSSRuleList> nonCharsetRules = StaticCSSRuleList::create();
260     unsigned ruleCount = length();
261     for (unsigned i = 0; i < ruleCount; ++i) {
262         CSSRule* rule = item(i);
263         if (rule->type() == CSSRule::CHARSET_RULE)
264             continue;
265         nonCharsetRules->rules().append(rule);
266     }
267     return nonCharsetRules.release();
268 }
269
270 unsigned CSSStyleSheet::insertRule(const String& ruleString, unsigned index, ExceptionState& exceptionState)
271 {
272     ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
273
274     if (Document* document = ownerDocument()) {
275         if (!document->contentSecurityPolicy()->allowStyleEval()) {
276             exceptionState.throwSecurityError(document->contentSecurityPolicy()->styleEvalDisabledErrorMessage());
277             return 0;
278         }
279     }
280
281     if (index > length()) {
282         exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length()) + ").");
283         return 0;
284     }
285     CSSParserContext context(m_contents->parserContext(), UseCounter::getFrom(this));
286     BisonCSSParser p(context);
287     RefPtr<StyleRuleBase> rule = p.parseRule(m_contents.get(), ruleString);
288
289     if (!rule) {
290         exceptionState.throwDOMException(SyntaxError, "Failed to parse the rule '" + ruleString + "'.");
291         return 0;
292     }
293     RuleMutationScope mutationScope(this);
294
295     bool success = m_contents->wrapperInsertRule(rule, index);
296     if (!success) {
297         exceptionState.throwDOMException(HierarchyRequestError, "Failed to insert the rule.");
298         return 0;
299     }
300     if (!m_childRuleCSSOMWrappers.isEmpty())
301         m_childRuleCSSOMWrappers.insert(index, RefPtr<CSSRule>());
302
303     return index;
304 }
305
306 unsigned CSSStyleSheet::insertRule(const String& rule, ExceptionState& exceptionState)
307 {
308     UseCounter::countDeprecation(activeExecutionContext(V8PerIsolateData::mainThreadIsolate()), UseCounter::CSSStyleSheetInsertRuleOptionalArg);
309     return insertRule(rule, 0, exceptionState);
310 }
311
312 void CSSStyleSheet::deleteRule(unsigned index, ExceptionState& exceptionState)
313 {
314     ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
315
316     if (index >= length()) {
317         exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length() - 1) + ").");
318         return;
319     }
320     RuleMutationScope mutationScope(this);
321
322     m_contents->wrapperDeleteRule(index);
323
324     if (!m_childRuleCSSOMWrappers.isEmpty()) {
325         if (m_childRuleCSSOMWrappers[index])
326             m_childRuleCSSOMWrappers[index]->setParentStyleSheet(0);
327         m_childRuleCSSOMWrappers.remove(index);
328     }
329 }
330
331 int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionState& exceptionState)
332 {
333     StringBuilder text;
334     text.append(selector);
335     text.appendLiteral(" { ");
336     text.append(style);
337     if (!style.isEmpty())
338         text.append(' ');
339     text.append('}');
340     insertRule(text.toString(), index, exceptionState);
341
342     // As per Microsoft documentation, always return -1.
343     return -1;
344 }
345
346 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionState& exceptionState)
347 {
348     return addRule(selector, style, length(), exceptionState);
349 }
350
351
352 PassRefPtr<CSSRuleList> CSSStyleSheet::cssRules()
353 {
354     if (!canAccessRules())
355         return 0;
356     if (!m_ruleListCSSOMWrapper)
357         m_ruleListCSSOMWrapper = adoptPtr(new StyleSheetCSSRuleList(this));
358     return m_ruleListCSSOMWrapper.get();
359 }
360
361 String CSSStyleSheet::href() const
362 {
363     return m_contents->originalURL();
364 }
365
366 KURL CSSStyleSheet::baseURL() const
367 {
368     return m_contents->baseURL();
369 }
370
371 bool CSSStyleSheet::isLoading() const
372 {
373     return m_contents->isLoading();
374 }
375
376 MediaList* CSSStyleSheet::media() const
377 {
378     if (!m_mediaQueries)
379         return 0;
380
381     if (!m_mediaCSSOMWrapper)
382         m_mediaCSSOMWrapper = MediaList::create(m_mediaQueries.get(), const_cast<CSSStyleSheet*>(this));
383     return m_mediaCSSOMWrapper.get();
384 }
385
386 CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const
387 {
388     return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
389 }
390
391 Document* CSSStyleSheet::ownerDocument() const
392 {
393     const CSSStyleSheet* root = this;
394     while (root->parentStyleSheet())
395         root = root->parentStyleSheet();
396     return root->ownerNode() ? &root->ownerNode()->document() : 0;
397 }
398
399 void CSSStyleSheet::clearChildRuleCSSOMWrappers()
400 {
401     m_childRuleCSSOMWrappers.clear();
402 }
403
404 bool CSSStyleSheet::sheetLoaded()
405 {
406     m_loadCompleted = m_ownerNode->sheetLoaded();
407     return m_loadCompleted;
408 }
409
410 void CSSStyleSheet::startLoadingDynamicSheet()
411 {
412     m_loadCompleted = false;
413     m_ownerNode->startLoadingDynamicSheet();
414 }
415
416 }