• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, 2010, 2012 Apple Inc. All rights reserved.
7  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
9  * Copyright (C) 2013 Google Inc. All rights reserved.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  */
26 
27 #include "config.h"
28 #include "core/dom/TreeScopeStyleSheetCollection.h"
29 
30 #include "core/css/CSSStyleSheet.h"
31 #include "core/css/StyleRuleImport.h"
32 #include "core/css/StyleSheetContents.h"
33 #include "core/css/invalidation/StyleSheetInvalidationAnalysis.h"
34 #include "core/css/resolver/StyleResolver.h"
35 #include "core/dom/Element.h"
36 #include "core/dom/StyleEngine.h"
37 #include "core/html/HTMLLinkElement.h"
38 #include "core/html/HTMLStyleElement.h"
39 
40 namespace WebCore {
41 
TreeScopeStyleSheetCollection(TreeScope & treeScope)42 TreeScopeStyleSheetCollection::TreeScopeStyleSheetCollection(TreeScope& treeScope)
43     : m_treeScope(treeScope)
44     , m_hadActiveLoadingStylesheet(false)
45     , m_usesRemUnits(false)
46 {
47 }
48 
addStyleSheetCandidateNode(Node * node,bool createdByParser)49 void TreeScopeStyleSheetCollection::addStyleSheetCandidateNode(Node* node, bool createdByParser)
50 {
51     if (!node->inDocument())
52         return;
53 
54     // Until the <body> exists, we have no choice but to compare document positions,
55     // since styles outside of the body and head continue to be shunted into the head
56     // (and thus can shift to end up before dynamically added DOM content that is also
57     // outside the body).
58     if (createdByParser && document().body())
59         m_styleSheetCandidateNodes.parserAdd(node);
60     else
61         m_styleSheetCandidateNodes.add(node);
62 
63     if (!isHTMLStyleElement(*node))
64         return;
65 
66     ContainerNode* scopingNode = toHTMLStyleElement(*node).scopingNode();
67     if (!isTreeScopeRoot(scopingNode))
68         m_scopingNodesForStyleScoped.add(scopingNode);
69 }
70 
removeStyleSheetCandidateNode(Node * node,ContainerNode * scopingNode)71 void TreeScopeStyleSheetCollection::removeStyleSheetCandidateNode(Node* node, ContainerNode* scopingNode)
72 {
73     m_styleSheetCandidateNodes.remove(node);
74 
75     if (!isTreeScopeRoot(scopingNode))
76         m_scopingNodesForStyleScoped.remove(scopingNode);
77 }
78 
compareStyleSheets(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet>> & oldStyleSheets,const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet>> & newStylesheets,WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>> & addedSheets)79 TreeScopeStyleSheetCollection::StyleResolverUpdateType TreeScopeStyleSheetCollection::compareStyleSheets(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& oldStyleSheets, const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStylesheets, WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& addedSheets)
80 {
81     unsigned newStyleSheetCount = newStylesheets.size();
82     unsigned oldStyleSheetCount = oldStyleSheets.size();
83     ASSERT(newStyleSheetCount >= oldStyleSheetCount);
84 
85     if (!newStyleSheetCount)
86         return Reconstruct;
87 
88     unsigned newIndex = 0;
89     for (unsigned oldIndex = 0; oldIndex < oldStyleSheetCount; ++oldIndex) {
90         while (oldStyleSheets[oldIndex] != newStylesheets[newIndex]) {
91             addedSheets.append(newStylesheets[newIndex]->contents());
92             if (++newIndex == newStyleSheetCount)
93                 return Reconstruct;
94         }
95         if (++newIndex == newStyleSheetCount)
96             return Reconstruct;
97     }
98     bool hasInsertions = !addedSheets.isEmpty();
99     while (newIndex < newStyleSheetCount) {
100         addedSheets.append(newStylesheets[newIndex]->contents());
101         ++newIndex;
102     }
103     // If all new sheets were added at the end of the list we can just add them to existing StyleResolver.
104     // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly.
105     return hasInsertions ? Reset : Additive;
106 }
107 
activeLoadingStyleSheetLoaded(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet>> & newStyleSheets)108 bool TreeScopeStyleSheetCollection::activeLoadingStyleSheetLoaded(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStyleSheets)
109 {
110     // StyleSheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
111     bool hasActiveLoadingStylesheet = false;
112     unsigned newStylesheetCount = newStyleSheets.size();
113     for (unsigned i = 0; i < newStylesheetCount; ++i) {
114         if (newStyleSheets[i]->isLoading())
115             hasActiveLoadingStylesheet = true;
116     }
117     if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
118         m_hadActiveLoadingStylesheet = false;
119         return true;
120     }
121     m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
122     return false;
123 }
124 
findFontFaceRulesFromStyleSheetContents(const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents>> & sheets,WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace>> & fontFaceRules)125 static bool findFontFaceRulesFromStyleSheetContents(const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& sheets, WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >& fontFaceRules)
126 {
127     bool hasFontFaceRule = false;
128 
129     for (unsigned i = 0; i < sheets.size(); ++i) {
130         ASSERT(sheets[i]);
131         if (sheets[i]->hasFontFaceRule()) {
132             // FIXME: We don't need this for styles in shadow tree.
133             sheets[i]->findFontFaceRules(fontFaceRules);
134             hasFontFaceRule = true;
135         }
136     }
137     return hasFontFaceRule;
138 }
139 
analyzeStyleSheetChange(StyleResolverUpdateMode updateMode,const StyleSheetCollection & newCollection,StyleSheetChange & change)140 void TreeScopeStyleSheetCollection::analyzeStyleSheetChange(StyleResolverUpdateMode updateMode, const StyleSheetCollection& newCollection, StyleSheetChange& change)
141 {
142     if (activeLoadingStyleSheetLoaded(newCollection.activeAuthorStyleSheets()))
143         return;
144 
145     if (updateMode != AnalyzedStyleUpdate)
146         return;
147 
148     // Find out which stylesheets are new.
149     WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> > addedSheets;
150     if (m_activeAuthorStyleSheets.size() <= newCollection.activeAuthorStyleSheets().size()) {
151         change.styleResolverUpdateType = compareStyleSheets(m_activeAuthorStyleSheets, newCollection.activeAuthorStyleSheets(), addedSheets);
152     } else {
153         StyleResolverUpdateType updateType = compareStyleSheets(newCollection.activeAuthorStyleSheets(), m_activeAuthorStyleSheets, addedSheets);
154         if (updateType != Additive) {
155             change.styleResolverUpdateType = updateType;
156         } else {
157             change.styleResolverUpdateType = Reset;
158             // If @font-face is removed, needs full style recalc.
159             if (findFontFaceRulesFromStyleSheetContents(addedSheets, change.fontFaceRulesToRemove))
160                 return;
161         }
162     }
163 
164     // FIXME: If styleResolverUpdateType is Reconstruct, we should return early here since
165     // we need to recalc the whole document. It's wrong to use StyleSheetInvalidationAnalysis since
166     // it only looks at the addedSheets.
167 
168     // No point in doing the analysis work if we're just going to recalc the whole document anyways.
169     // This needs to be done after the compareStyleSheets calls above to ensure we don't throw away
170     // the StyleResolver if we don't need to.
171     if (document().hasPendingForcedStyleRecalc())
172         return;
173 
174     // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
175     if (!document().body() || document().hasNodesWithPlaceholderStyle())
176         return;
177     StyleSheetInvalidationAnalysis invalidationAnalysis(addedSheets);
178     if (invalidationAnalysis.dirtiesAllStyle())
179         return;
180     invalidationAnalysis.invalidateStyle(document());
181     change.requiresFullStyleRecalc = false;
182     return;
183 }
184 
clearMediaQueryRuleSetStyleSheets()185 void TreeScopeStyleSheetCollection::clearMediaQueryRuleSetStyleSheets()
186 {
187     for (size_t i = 0; i < m_activeAuthorStyleSheets.size(); ++i) {
188         StyleSheetContents* contents = m_activeAuthorStyleSheets[i]->contents();
189         if (contents->hasMediaQueries())
190             contents->clearRuleSet();
191     }
192 }
193 
enableExitTransitionStylesheets()194 void TreeScopeStyleSheetCollection::enableExitTransitionStylesheets()
195 {
196     DocumentOrderedList::iterator begin = m_styleSheetCandidateNodes.begin();
197     DocumentOrderedList::iterator end = m_styleSheetCandidateNodes.end();
198     for (DocumentOrderedList::iterator it = begin; it != end; ++it) {
199         Node* node = *it;
200         if (isHTMLLinkElement(*node))
201             toHTMLLinkElement(node)->enableIfExitTransitionStyle();
202     }
203 }
204 
resetAllRuleSetsInTreeScope(StyleResolver * styleResolver)205 void TreeScopeStyleSheetCollection::resetAllRuleSetsInTreeScope(StyleResolver* styleResolver)
206 {
207     // FIXME: If many web developers use style scoped, implement reset RuleSets in per-scoping node manner.
208     if (DocumentOrderedList* styleScopedScopingNodes = scopingNodesForStyleScoped()) {
209         for (DocumentOrderedList::iterator it = styleScopedScopingNodes->begin(); it != styleScopedScopingNodes->end(); ++it)
210             styleResolver->resetAuthorStyle(toContainerNode(*it));
211     }
212     if (ListHashSet<Node*, 4>* removedNodes = scopingNodesRemoved()) {
213         for (ListHashSet<Node*, 4>::iterator it = removedNodes->begin(); it != removedNodes->end(); ++it)
214             styleResolver->resetAuthorStyle(toContainerNode(*it));
215     }
216     styleResolver->resetAuthorStyle(toContainerNode(&m_treeScope.rootNode()));
217 }
218 
styleSheetsUseRemUnits(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet>> & sheets)219 static bool styleSheetsUseRemUnits(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& sheets)
220 {
221     for (unsigned i = 0; i < sheets.size(); ++i) {
222         if (sheets[i]->contents()->usesRemUnits())
223             return true;
224     }
225     return false;
226 }
227 
updateUsesRemUnits()228 void TreeScopeStyleSheetCollection::updateUsesRemUnits()
229 {
230     m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets);
231 }
232 
233 }
234