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