Remove the memory instrumentation code
[WebKit-https.git] / Source / WebCore / dom / DocumentStyleSheetCollection.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  *           (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All rights reserved.
7  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8  * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
9  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
10  * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  */
27
28 #include "config.h"
29 #include "DocumentStyleSheetCollection.h"
30
31 #include "CSSStyleSheet.h"
32 #include "DOMWrapperWorld.h"
33 #include "Document.h"
34 #include "Element.h"
35 #include "HTMLIFrameElement.h"
36 #include "HTMLLinkElement.h"
37 #include "HTMLNames.h"
38 #include "HTMLStyleElement.h"
39 #include "Page.h"
40 #include "PageGroup.h"
41 #include "ProcessingInstruction.h"
42 #include "SVGNames.h"
43 #include "SVGStyleElement.h"
44 #include "SelectorChecker.h"
45 #include "Settings.h"
46 #include "StyleInvalidationAnalysis.h"
47 #include "StyleResolver.h"
48 #include "StyleSheetContents.h"
49 #include "StyleSheetList.h"
50 #include "UserContentURLPattern.h"
51
52 namespace WebCore {
53
54 using namespace HTMLNames;
55
56 DocumentStyleSheetCollection::DocumentStyleSheetCollection(Document* document)
57     : m_document(document)
58     , m_pendingStylesheets(0)
59     , m_injectedStyleSheetCacheValid(false)
60     , m_hadActiveLoadingStylesheet(false)
61     , m_needsUpdateActiveStylesheetsOnStyleRecalc(false)
62     , m_usesSiblingRules(false)
63     , m_usesSiblingRulesOverride(false)
64     , m_usesFirstLineRules(false)
65     , m_usesFirstLetterRules(false)
66     , m_usesBeforeAfterRules(false)
67     , m_usesBeforeAfterRulesOverride(false)
68     , m_usesRemUnits(false)
69 {
70 }
71
72 DocumentStyleSheetCollection::~DocumentStyleSheetCollection()
73 {
74     if (m_pageUserSheet)
75         m_pageUserSheet->clearOwnerNode();
76     for (unsigned i = 0; i < m_injectedUserStyleSheets.size(); ++i)
77         m_injectedUserStyleSheets[i]->clearOwnerNode();
78     for (unsigned i = 0; i < m_injectedAuthorStyleSheets.size(); ++i)
79         m_injectedAuthorStyleSheets[i]->clearOwnerNode();
80     for (unsigned i = 0; i < m_userStyleSheets.size(); ++i)
81         m_userStyleSheets[i]->clearOwnerNode();
82     for (unsigned i = 0; i < m_authorStyleSheets.size(); ++i)
83         m_authorStyleSheets[i]->clearOwnerNode();
84 }
85
86 void DocumentStyleSheetCollection::combineCSSFeatureFlags()
87 {
88     // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after).
89     StyleResolver* styleResolver = m_document->styleResolver();
90     m_usesSiblingRules = m_usesSiblingRules || styleResolver->usesSiblingRules();
91     m_usesFirstLineRules = m_usesFirstLineRules || styleResolver->usesFirstLineRules();
92     m_usesBeforeAfterRules = m_usesBeforeAfterRules || styleResolver->usesBeforeAfterRules();
93 }
94
95 void DocumentStyleSheetCollection::resetCSSFeatureFlags()
96 {
97     StyleResolver* styleResolver = m_document->styleResolver();
98     m_usesSiblingRules = styleResolver->usesSiblingRules();
99     m_usesFirstLineRules = styleResolver->usesFirstLineRules();
100     m_usesBeforeAfterRules = styleResolver->usesBeforeAfterRules();
101 }
102
103 CSSStyleSheet* DocumentStyleSheetCollection::pageUserSheet()
104 {
105     if (m_pageUserSheet)
106         return m_pageUserSheet.get();
107     
108     Page* owningPage = m_document->page();
109     if (!owningPage)
110         return 0;
111     
112     String userSheetText = owningPage->userStyleSheet();
113     if (userSheetText.isEmpty())
114         return 0;
115     
116     // Parse the sheet and cache it.
117     m_pageUserSheet = CSSStyleSheet::createInline(m_document, m_document->settings()->userStyleSheetLocation());
118     m_pageUserSheet->contents()->setIsUserStyleSheet(true);
119     m_pageUserSheet->contents()->parseString(userSheetText);
120     return m_pageUserSheet.get();
121 }
122
123 void DocumentStyleSheetCollection::clearPageUserSheet()
124 {
125     if (m_pageUserSheet) {
126         m_pageUserSheet = 0;
127         m_document->styleResolverChanged(DeferRecalcStyle);
128     }
129 }
130
131 void DocumentStyleSheetCollection::updatePageUserSheet()
132 {
133     clearPageUserSheet();
134     if (pageUserSheet())
135         m_document->styleResolverChanged(RecalcStyleImmediately);
136 }
137
138 const Vector<RefPtr<CSSStyleSheet> >& DocumentStyleSheetCollection::injectedUserStyleSheets() const
139 {
140     updateInjectedStyleSheetCache();
141     return m_injectedUserStyleSheets;
142 }
143
144 const Vector<RefPtr<CSSStyleSheet> >& DocumentStyleSheetCollection::injectedAuthorStyleSheets() const
145 {
146     updateInjectedStyleSheetCache();
147     return m_injectedAuthorStyleSheets;
148 }
149
150 void DocumentStyleSheetCollection::updateInjectedStyleSheetCache() const
151 {
152     if (m_injectedStyleSheetCacheValid)
153         return;
154     m_injectedStyleSheetCacheValid = true;
155     m_injectedUserStyleSheets.clear();
156     m_injectedAuthorStyleSheets.clear();
157
158     Page* owningPage = m_document->page();
159     if (!owningPage)
160         return;
161         
162     const PageGroup& pageGroup = owningPage->group();
163     const UserStyleSheetMap* sheetsMap = pageGroup.userStyleSheets();
164     if (!sheetsMap)
165         return;
166
167     UserStyleSheetMap::const_iterator end = sheetsMap->end();
168     for (UserStyleSheetMap::const_iterator it = sheetsMap->begin(); it != end; ++it) {
169         const UserStyleSheetVector* sheets = it->value.get();
170         for (unsigned i = 0; i < sheets->size(); ++i) {
171             const UserStyleSheet* sheet = sheets->at(i).get();
172             if (sheet->injectedFrames() == InjectInTopFrameOnly && m_document->ownerElement())
173                 continue;
174             if (!UserContentURLPattern::matchesPatterns(m_document->url(), sheet->whitelist(), sheet->blacklist()))
175                 continue;
176             RefPtr<CSSStyleSheet> groupSheet = CSSStyleSheet::createInline(const_cast<Document*>(m_document), sheet->url());
177             bool isUserStyleSheet = sheet->level() == UserStyleUserLevel;
178             if (isUserStyleSheet)
179                 m_injectedUserStyleSheets.append(groupSheet);
180             else
181                 m_injectedAuthorStyleSheets.append(groupSheet);
182             groupSheet->contents()->setIsUserStyleSheet(isUserStyleSheet);
183             groupSheet->contents()->parseString(sheet->source());
184         }
185     }
186 }
187
188 void DocumentStyleSheetCollection::invalidateInjectedStyleSheetCache()
189 {
190     m_injectedStyleSheetCacheValid = false;
191     m_document->styleResolverChanged(DeferRecalcStyle);
192 }
193
194 void DocumentStyleSheetCollection::addAuthorSheet(PassRefPtr<StyleSheetContents> authorSheet)
195 {
196     ASSERT(!authorSheet->isUserStyleSheet());
197     m_authorStyleSheets.append(CSSStyleSheet::create(authorSheet, m_document));
198     m_document->styleResolverChanged(RecalcStyleImmediately);
199 }
200
201 void DocumentStyleSheetCollection::addUserSheet(PassRefPtr<StyleSheetContents> userSheet)
202 {
203     ASSERT(userSheet->isUserStyleSheet());
204     m_userStyleSheets.append(CSSStyleSheet::create(userSheet, m_document));
205     m_document->styleResolverChanged(RecalcStyleImmediately);
206 }
207
208 // This method is called whenever a top-level stylesheet has finished loading.
209 void DocumentStyleSheetCollection::removePendingSheet(RemovePendingSheetNotificationType notification)
210 {
211     // Make sure we knew this sheet was pending, and that our count isn't out of sync.
212     ASSERT(m_pendingStylesheets > 0);
213
214     m_pendingStylesheets--;
215     
216 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
217     if (!ownerElement())
218         printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
219 #endif
220
221     if (m_pendingStylesheets)
222         return;
223
224     if (notification == RemovePendingSheetNotifyLater) {
225         m_document->setNeedsNotifyRemoveAllPendingStylesheet();
226         return;
227     }
228     
229     m_document->didRemoveAllPendingStylesheet();
230 }
231
232 void DocumentStyleSheetCollection::addStyleSheetCandidateNode(Node* node, bool createdByParser)
233 {
234     if (!node->inDocument())
235         return;
236     
237     // Until the <body> exists, we have no choice but to compare document positions,
238     // since styles outside of the body and head continue to be shunted into the head
239     // (and thus can shift to end up before dynamically added DOM content that is also
240     // outside the body).
241     if ((createdByParser && m_document->body()) || m_styleSheetCandidateNodes.isEmpty()) {
242         m_styleSheetCandidateNodes.add(node);
243         return;
244     }
245
246     // Determine an appropriate insertion point.
247     StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin();
248     StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end();
249     StyleSheetCandidateListHashSet::iterator it = end;
250     Node* followingNode = 0;
251     do {
252         --it;
253         Node* n = *it;
254         unsigned short position = n->compareDocumentPosition(node);
255         if (position == Node::DOCUMENT_POSITION_FOLLOWING) {
256             m_styleSheetCandidateNodes.insertBefore(followingNode, node);
257             return;
258         }
259         followingNode = n;
260     } while (it != begin);
261     
262     m_styleSheetCandidateNodes.insertBefore(followingNode, node);
263 }
264
265 void DocumentStyleSheetCollection::removeStyleSheetCandidateNode(Node* node)
266 {
267     m_styleSheetCandidateNodes.remove(node);
268 }
269
270 void DocumentStyleSheetCollection::collectActiveStyleSheets(Vector<RefPtr<StyleSheet> >& sheets)
271 {
272     if (m_document->settings() && !m_document->settings()->authorAndUserStylesEnabled())
273         return;
274
275     StyleSheetCandidateListHashSet::iterator begin = m_styleSheetCandidateNodes.begin();
276     StyleSheetCandidateListHashSet::iterator end = m_styleSheetCandidateNodes.end();
277     for (StyleSheetCandidateListHashSet::iterator it = begin; it != end; ++it) {
278         Node* n = *it;
279         StyleSheet* sheet = 0;
280         if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) {
281             // Processing instruction (XML documents only).
282             // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
283             ProcessingInstruction* pi = static_cast<ProcessingInstruction*>(n);
284             sheet = pi->sheet();
285 #if ENABLE(XSLT)
286             // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806>
287             if (pi->isXSL() && !m_document->transformSourceDocument()) {
288                 // Don't apply XSL transforms until loading is finished.
289                 if (!m_document->parsing())
290                     m_document->applyXSLTransform(pi);
291                 return;
292             }
293 #endif
294         } else if ((n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag)))
295 #if ENABLE(SVG)
296                    ||  (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
297 #endif
298                    ) {
299             Element* e = toElement(n);
300             AtomicString title = e->getAttribute(titleAttr);
301             bool enabledViaScript = false;
302             if (e->hasLocalName(linkTag)) {
303                 // <LINK> element
304                 HTMLLinkElement* linkElement = static_cast<HTMLLinkElement*>(n);
305                 if (linkElement->isDisabled())
306                     continue;
307                 enabledViaScript = linkElement->isEnabledViaScript();
308                 if (linkElement->styleSheetIsLoading()) {
309                     // it is loading but we should still decide which style sheet set to use
310                     if (!enabledViaScript && !title.isEmpty() && m_preferredStylesheetSetName.isEmpty()) {
311                         const AtomicString& rel = e->getAttribute(relAttr);
312                         if (!rel.contains("alternate")) {
313                             m_preferredStylesheetSetName = title;
314                             m_selectedStylesheetSetName = title;
315                         }
316                     }
317                     continue;
318                 }
319                 if (!linkElement->sheet())
320                     title = nullAtom;
321             }
322             // Get the current preferred styleset. This is the
323             // set of sheets that will be enabled.
324 #if ENABLE(SVG)
325             if (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
326                 sheet = static_cast<SVGStyleElement*>(n)->sheet();
327             else
328 #endif
329             if (e->hasLocalName(linkTag))
330                 sheet = static_cast<HTMLLinkElement*>(n)->sheet();
331             else
332                 // <STYLE> element
333                 sheet = static_cast<HTMLStyleElement*>(n)->sheet();
334             // Check to see if this sheet belongs to a styleset
335             // (thus making it PREFERRED or ALTERNATE rather than
336             // PERSISTENT).
337             AtomicString rel = e->getAttribute(relAttr);
338             if (!enabledViaScript && !title.isEmpty()) {
339                 // Yes, we have a title.
340                 if (m_preferredStylesheetSetName.isEmpty()) {
341                     // No preferred set has been established. If
342                     // we are NOT an alternate sheet, then establish
343                     // us as the preferred set. Otherwise, just ignore
344                     // this sheet.
345                     if (e->hasLocalName(styleTag) || !rel.contains("alternate"))
346                         m_preferredStylesheetSetName = m_selectedStylesheetSetName = title;
347                 }
348                 if (title != m_preferredStylesheetSetName)
349                     sheet = 0;
350             }
351
352             if (rel.contains("alternate") && title.isEmpty())
353                 sheet = 0;
354         }
355         if (sheet)
356             sheets.append(sheet);
357     }
358 }
359
360 void DocumentStyleSheetCollection::analyzeStyleSheetChange(UpdateFlag updateFlag, const Vector<RefPtr<CSSStyleSheet> >& newStylesheets, StyleResolverUpdateType& styleResolverUpdateType, bool& requiresFullStyleRecalc)
361 {
362     styleResolverUpdateType = Reconstruct;
363     requiresFullStyleRecalc = true;
364     
365     // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
366     bool hasActiveLoadingStylesheet = false;
367     unsigned newStylesheetCount = newStylesheets.size();
368     for (unsigned i = 0; i < newStylesheetCount; ++i) {
369         if (newStylesheets[i]->isLoading())
370             hasActiveLoadingStylesheet = true;
371     }
372     if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
373         m_hadActiveLoadingStylesheet = false;
374         return;
375     }
376     m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
377
378     if (updateFlag != OptimizedUpdate)
379         return;
380     if (!m_document->styleResolverIfExists())
381         return;
382
383     // Find out which stylesheets are new.
384     unsigned oldStylesheetCount = m_activeAuthorStyleSheets.size();
385     if (newStylesheetCount < oldStylesheetCount)
386         return;
387     Vector<StyleSheetContents*> addedSheets;
388     unsigned newIndex = 0;
389     for (unsigned oldIndex = 0; oldIndex < oldStylesheetCount; ++oldIndex) {
390         if (newIndex >= newStylesheetCount)
391             return;
392         while (m_activeAuthorStyleSheets[oldIndex] != newStylesheets[newIndex]) {
393             addedSheets.append(newStylesheets[newIndex]->contents());
394             ++newIndex;
395             if (newIndex == newStylesheetCount)
396                 return;
397         }
398         ++newIndex;
399     }
400     bool hasInsertions = !addedSheets.isEmpty();
401     while (newIndex < newStylesheetCount) {
402         addedSheets.append(newStylesheets[newIndex]->contents());
403         ++newIndex;
404     }
405     // If all new sheets were added at the end of the list we can just add them to existing StyleResolver.
406     // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly.
407     styleResolverUpdateType = hasInsertions ? Reset : Additive;
408
409     // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
410     if (!m_document->body() || m_document->hasNodesWithPlaceholderStyle())
411         return;
412     StyleInvalidationAnalysis invalidationAnalysis(addedSheets);
413     if (invalidationAnalysis.dirtiesAllStyle())
414         return;
415     invalidationAnalysis.invalidateStyle(m_document);
416     requiresFullStyleRecalc = false;
417 }
418
419 static bool styleSheetsUseRemUnits(const Vector<RefPtr<CSSStyleSheet> >& sheets)
420 {
421     for (unsigned i = 0; i < sheets.size(); ++i) {
422         if (sheets[i]->contents()->usesRemUnits())
423             return true;
424     }
425     return false;
426 }
427
428 static void filterEnabledCSSStyleSheets(Vector<RefPtr<CSSStyleSheet> >& result, const Vector<RefPtr<StyleSheet> >& sheets)
429 {
430     for (unsigned i = 0; i < sheets.size(); ++i) {
431         if (!sheets[i]->isCSSStyleSheet())
432             continue;
433         if (sheets[i]->disabled())
434             continue;
435         result.append(static_cast<CSSStyleSheet*>(sheets[i].get()));
436     }
437 }
438
439 static void collectActiveCSSStyleSheetsFromSeamlessParents(Vector<RefPtr<CSSStyleSheet> >& sheets, Document* document)
440 {
441     HTMLIFrameElement* seamlessParentIFrame = document->seamlessParentIFrame();
442     if (!seamlessParentIFrame)
443         return;
444     sheets.append(seamlessParentIFrame->document()->styleSheetCollection()->activeAuthorStyleSheets());
445 }
446
447 bool DocumentStyleSheetCollection::updateActiveStyleSheets(UpdateFlag updateFlag)
448 {
449     if (m_document->inStyleRecalc()) {
450         // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
451         // https://bugs.webkit.org/show_bug.cgi?id=54344
452         // FIXME: This should be fixed in SVG and the call site replaced by ASSERT(!m_inStyleRecalc).
453         m_needsUpdateActiveStylesheetsOnStyleRecalc = true;
454         m_document->scheduleForcedStyleRecalc();
455         return false;
456
457     }
458     if (!m_document->renderer() || !m_document->attached())
459         return false;
460
461     Vector<RefPtr<StyleSheet> > activeStyleSheets;
462     collectActiveStyleSheets(activeStyleSheets);
463
464     Vector<RefPtr<CSSStyleSheet> > activeCSSStyleSheets;
465     activeCSSStyleSheets.append(injectedAuthorStyleSheets());
466     activeCSSStyleSheets.append(documentAuthorStyleSheets());
467     collectActiveCSSStyleSheetsFromSeamlessParents(activeCSSStyleSheets, m_document);
468     filterEnabledCSSStyleSheets(activeCSSStyleSheets, activeStyleSheets);
469
470     StyleResolverUpdateType styleResolverUpdateType;
471     bool requiresFullStyleRecalc;
472     analyzeStyleSheetChange(updateFlag, activeCSSStyleSheets, styleResolverUpdateType, requiresFullStyleRecalc);
473
474     if (styleResolverUpdateType == Reconstruct)
475         m_document->clearStyleResolver();
476     else {
477         StyleResolver* styleResolver = m_document->styleResolver();
478         if (styleResolverUpdateType == Reset) {
479             styleResolver->ruleSets().resetAuthorStyle();
480             styleResolver->appendAuthorStyleSheets(0, activeCSSStyleSheets);
481         } else {
482             ASSERT(styleResolverUpdateType == Additive);
483             styleResolver->appendAuthorStyleSheets(m_activeAuthorStyleSheets.size(), activeCSSStyleSheets);
484         }
485         resetCSSFeatureFlags();
486     }
487     m_activeAuthorStyleSheets.swap(activeCSSStyleSheets);
488     m_styleSheetsForStyleSheetList.swap(activeStyleSheets);
489
490     m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets);
491     m_needsUpdateActiveStylesheetsOnStyleRecalc = false;
492
493     m_document->notifySeamlessChildDocumentsOfStylesheetUpdate();
494
495     return requiresFullStyleRecalc;
496 }
497
498 }