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