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