Upstream version 9.38.198.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 "bindings/core/v8/ExceptionState.h"
25 #include "bindings/core/v8/V8Binding.h"
26 #include "bindings/core/v8/V8PerIsolateData.h"
27 #include "core/HTMLNames.h"
28 #include "core/SVGNames.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 blink {
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 ENABLE(ASSERT)
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     ScriptWrappable::init(this);
127     m_contents->registerClient(this);
128 }
129
130 CSSStyleSheet::CSSStyleSheet(PassRefPtrWillBeRawPtr<StyleSheetContents> contents, Node* ownerNode, bool isInlineStylesheet, const TextPosition& startPosition)
131     : m_contents(contents)
132     , m_isInlineStylesheet(isInlineStylesheet)
133     , m_isDisabled(false)
134     , m_ownerNode(ownerNode)
135     , m_ownerRule(nullptr)
136     , m_startPosition(startPosition)
137     , m_loadCompleted(false)
138 {
139     ScriptWrappable::init(this);
140     ASSERT(isAcceptableCSSStyleSheetParent(ownerNode));
141     m_contents->registerClient(this);
142 }
143
144 CSSStyleSheet::~CSSStyleSheet()
145 {
146     // With oilpan the parent style sheet pointer is strong and the sheet and
147     // its RuleCSSOMWrappers die together and we don't need to clear them here.
148     // Also with oilpan the StyleSheetContents client pointers are weak and
149     // therefore do not need to be cleared here.
150 #if !ENABLE(OILPAN)
151     // For style rules outside the document, .parentStyleSheet can become null even if the style rule
152     // is still observable from JavaScript. This matches the behavior of .parentNode for nodes, but
153     // it's not ideal because it makes the CSSOM's behavior depend on the timing of garbage collection.
154     for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
155         if (m_childRuleCSSOMWrappers[i])
156             m_childRuleCSSOMWrappers[i]->setParentStyleSheet(0);
157     }
158
159     if (m_mediaCSSOMWrapper)
160         m_mediaCSSOMWrapper->clearParentStyleSheet();
161
162     m_contents->unregisterClient(this);
163 #endif
164 }
165
166 void CSSStyleSheet::willMutateRules()
167 {
168     InspectorInstrumentation::willMutateRules(this);
169
170     // If we are the only client it is safe to mutate.
171     if (m_contents->clientSize() <= 1 && !m_contents->isInMemoryCache()) {
172         m_contents->clearRuleSet();
173         if (Document* document = ownerDocument())
174             m_contents->removeSheetFromCache(document);
175         m_contents->setMutable();
176         return;
177     }
178     // Only cacheable stylesheets should have multiple clients.
179     ASSERT(m_contents->isCacheable());
180
181     // Copy-on-write.
182     m_contents->unregisterClient(this);
183     m_contents = m_contents->copy();
184     m_contents->registerClient(this);
185
186     m_contents->setMutable();
187
188     // Any existing CSSOM wrappers need to be connected to the copied child rules.
189     reattachChildRuleCSSOMWrappers();
190 }
191
192 void CSSStyleSheet::didMutateRules()
193 {
194     ASSERT(m_contents->isMutable());
195     ASSERT(m_contents->clientSize() <= 1);
196
197     InspectorInstrumentation::didMutateRules(this);
198     didMutate(PartialRuleUpdate);
199 }
200
201 void CSSStyleSheet::didMutate(StyleSheetUpdateType updateType)
202 {
203     Document* owner = ownerDocument();
204     if (!owner)
205         return;
206
207     // Need FullStyleUpdate when insertRule or deleteRule,
208     // because StyleSheetCollection::analyzeStyleSheetChange cannot detect partial rule update.
209     StyleResolverUpdateMode updateMode = updateType != PartialRuleUpdate ? AnalyzedStyleUpdate : FullStyleUpdate;
210     owner->modifiedStyleSheet(this, updateMode);
211 }
212
213 void CSSStyleSheet::reattachChildRuleCSSOMWrappers()
214 {
215     for (unsigned i = 0; i < m_childRuleCSSOMWrappers.size(); ++i) {
216         if (!m_childRuleCSSOMWrappers[i])
217             continue;
218         m_childRuleCSSOMWrappers[i]->reattach(m_contents->ruleAt(i));
219     }
220 }
221
222 void CSSStyleSheet::setDisabled(bool disabled)
223 {
224     if (disabled == m_isDisabled)
225         return;
226     m_isDisabled = disabled;
227
228     didMutate();
229 }
230
231 void CSSStyleSheet::setMediaQueries(PassRefPtrWillBeRawPtr<MediaQuerySet> mediaQueries)
232 {
233     m_mediaQueries = mediaQueries;
234     if (m_mediaCSSOMWrapper && m_mediaQueries)
235         m_mediaCSSOMWrapper->reattach(m_mediaQueries.get());
236
237     // Add warning message to inspector whenever dpi/dpcm values are used for "screen" media.
238     reportMediaQueryWarningIfNeeded(ownerDocument(), m_mediaQueries.get());
239 }
240
241 unsigned CSSStyleSheet::length() const
242 {
243     return m_contents->ruleCount();
244 }
245
246 CSSRule* CSSStyleSheet::item(unsigned index)
247 {
248     unsigned ruleCount = length();
249     if (index >= ruleCount)
250         return 0;
251
252     if (m_childRuleCSSOMWrappers.isEmpty())
253         m_childRuleCSSOMWrappers.grow(ruleCount);
254     ASSERT(m_childRuleCSSOMWrappers.size() == ruleCount);
255
256     RefPtrWillBeMember<CSSRule>& cssRule = m_childRuleCSSOMWrappers[index];
257     if (!cssRule) {
258         if (index == 0 && m_contents->hasCharsetRule()) {
259             ASSERT(!m_contents->ruleAt(0));
260             cssRule = CSSCharsetRule::create(this, m_contents->encodingFromCharsetRule());
261         } else
262             cssRule = m_contents->ruleAt(index)->createCSSOMWrapper(this);
263     }
264     return cssRule.get();
265 }
266
267 void CSSStyleSheet::clearOwnerNode()
268 {
269     didMutate(EntireStyleSheetUpdate);
270     if (m_ownerNode)
271         m_contents->unregisterClient(this);
272     m_ownerNode = nullptr;
273 }
274
275 bool CSSStyleSheet::canAccessRules() const
276 {
277     if (m_isInlineStylesheet)
278         return true;
279     KURL baseURL = m_contents->baseURL();
280     if (baseURL.isEmpty())
281         return true;
282     Document* document = ownerDocument();
283     if (!document)
284         return true;
285     if (document->securityOrigin()->canRequest(baseURL))
286         return true;
287     return false;
288 }
289
290 PassRefPtrWillBeRawPtr<CSSRuleList> CSSStyleSheet::rules()
291 {
292     if (!canAccessRules())
293         return nullptr;
294     // IE behavior.
295     RefPtrWillBeRawPtr<StaticCSSRuleList> nonCharsetRules(StaticCSSRuleList::create());
296     unsigned ruleCount = length();
297     for (unsigned i = 0; i < ruleCount; ++i) {
298         CSSRule* rule = item(i);
299         if (rule->type() == CSSRule::CHARSET_RULE)
300             continue;
301         nonCharsetRules->rules().append(rule);
302     }
303     return nonCharsetRules.release();
304 }
305
306 unsigned CSSStyleSheet::insertRule(const String& ruleString, unsigned index, ExceptionState& exceptionState)
307 {
308     ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
309
310     if (index > length()) {
311         exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length()) + ").");
312         return 0;
313     }
314     CSSParserContext context(m_contents->parserContext(), UseCounter::getFrom(this));
315     BisonCSSParser p(context);
316     RefPtrWillBeRawPtr<StyleRuleBase> rule = p.parseRule(m_contents.get(), ruleString);
317
318     if (!rule) {
319         exceptionState.throwDOMException(SyntaxError, "Failed to parse the rule '" + ruleString + "'.");
320         return 0;
321     }
322     RuleMutationScope mutationScope(this);
323
324     bool success = m_contents->wrapperInsertRule(rule, index);
325     if (!success) {
326         exceptionState.throwDOMException(HierarchyRequestError, "Failed to insert the rule.");
327         return 0;
328     }
329     if (!m_childRuleCSSOMWrappers.isEmpty())
330         m_childRuleCSSOMWrappers.insert(index, RefPtrWillBeMember<CSSRule>(nullptr));
331
332     return index;
333 }
334
335 unsigned CSSStyleSheet::insertRule(const String& rule, ExceptionState& exceptionState)
336 {
337     UseCounter::countDeprecation(callingExecutionContext(V8PerIsolateData::mainThreadIsolate()), UseCounter::CSSStyleSheetInsertRuleOptionalArg);
338     return insertRule(rule, 0, exceptionState);
339 }
340
341 void CSSStyleSheet::deleteRule(unsigned index, ExceptionState& exceptionState)
342 {
343     ASSERT(m_childRuleCSSOMWrappers.isEmpty() || m_childRuleCSSOMWrappers.size() == m_contents->ruleCount());
344
345     if (index >= length()) {
346         exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is larger than the maximum index (" + String::number(length() - 1) + ").");
347         return;
348     }
349     RuleMutationScope mutationScope(this);
350
351     m_contents->wrapperDeleteRule(index);
352
353     if (!m_childRuleCSSOMWrappers.isEmpty()) {
354         if (m_childRuleCSSOMWrappers[index])
355             m_childRuleCSSOMWrappers[index]->setParentStyleSheet(0);
356         m_childRuleCSSOMWrappers.remove(index);
357     }
358 }
359
360 int CSSStyleSheet::addRule(const String& selector, const String& style, int index, ExceptionState& exceptionState)
361 {
362     StringBuilder text;
363     text.append(selector);
364     text.appendLiteral(" { ");
365     text.append(style);
366     if (!style.isEmpty())
367         text.append(' ');
368     text.append('}');
369     insertRule(text.toString(), index, exceptionState);
370
371     // As per Microsoft documentation, always return -1.
372     return -1;
373 }
374
375 int CSSStyleSheet::addRule(const String& selector, const String& style, ExceptionState& exceptionState)
376 {
377     return addRule(selector, style, length(), exceptionState);
378 }
379
380
381 PassRefPtrWillBeRawPtr<CSSRuleList> CSSStyleSheet::cssRules()
382 {
383     if (!canAccessRules())
384         return nullptr;
385     if (!m_ruleListCSSOMWrapper)
386         m_ruleListCSSOMWrapper = StyleSheetCSSRuleList::create(this);
387     return m_ruleListCSSOMWrapper.get();
388 }
389
390 String CSSStyleSheet::href() const
391 {
392     return m_contents->originalURL();
393 }
394
395 KURL CSSStyleSheet::baseURL() const
396 {
397     return m_contents->baseURL();
398 }
399
400 bool CSSStyleSheet::isLoading() const
401 {
402     return m_contents->isLoading();
403 }
404
405 MediaList* CSSStyleSheet::media() const
406 {
407     if (!m_mediaQueries)
408         return 0;
409
410     if (!m_mediaCSSOMWrapper)
411         m_mediaCSSOMWrapper = MediaList::create(m_mediaQueries.get(), const_cast<CSSStyleSheet*>(this));
412     return m_mediaCSSOMWrapper.get();
413 }
414
415 CSSStyleSheet* CSSStyleSheet::parentStyleSheet() const
416 {
417     return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
418 }
419
420 Document* CSSStyleSheet::ownerDocument() const
421 {
422     const CSSStyleSheet* root = this;
423     while (root->parentStyleSheet())
424         root = root->parentStyleSheet();
425     return root->ownerNode() ? &root->ownerNode()->document() : 0;
426 }
427
428 void CSSStyleSheet::clearChildRuleCSSOMWrappers()
429 {
430     m_childRuleCSSOMWrappers.clear();
431 }
432
433 bool CSSStyleSheet::sheetLoaded()
434 {
435     ASSERT(m_ownerNode);
436     setLoadCompleted(m_ownerNode->sheetLoaded());
437     return m_loadCompleted;
438 }
439
440 void CSSStyleSheet::startLoadingDynamicSheet()
441 {
442     setLoadCompleted(false);
443     m_ownerNode->startLoadingDynamicSheet();
444 }
445
446 void CSSStyleSheet::setLoadCompleted(bool completed)
447 {
448     if (completed == m_loadCompleted)
449         return;
450
451     m_loadCompleted = completed;
452
453     if (completed)
454         m_contents->clientLoadCompleted(this);
455     else
456         m_contents->clientLoadStarted(this);
457 }
458
459 void CSSStyleSheet::trace(Visitor* visitor)
460 {
461     visitor->trace(m_contents);
462     visitor->trace(m_mediaQueries);
463     visitor->trace(m_ownerNode);
464     visitor->trace(m_ownerRule);
465     visitor->trace(m_mediaCSSOMWrapper);
466     visitor->trace(m_childRuleCSSOMWrappers);
467     visitor->trace(m_ruleListCSSOMWrapper);
468     StyleSheet::trace(visitor);
469 }
470
471 }