1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2001 Dirk Mueller (mueller (at) kde.org) 5 * (C) 2006 Alexey Proskuryakov (ap (at) 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/StyleSheetCollection.h" 29 30 #include "core/css/CSSStyleSheet.h" 31 #include "core/css/StyleInvalidationAnalysis.h" 32 #include "core/css/StyleRuleImport.h" 33 #include "core/css/StyleSheetContents.h" 34 #include "core/css/resolver/StyleResolver.h" 35 #include "core/dom/Element.h" 36 #include "core/dom/StyleEngine.h" 37 #include "core/html/HTMLStyleElement.h" 38 #include "core/frame/Settings.h" 39 40 namespace WebCore { 41 42 StyleSheetCollectionBase::StyleSheetCollectionBase() 43 { 44 } 45 46 StyleSheetCollectionBase::~StyleSheetCollectionBase() 47 { 48 } 49 50 void StyleSheetCollectionBase::swap(StyleSheetCollectionBase& other) 51 { 52 m_styleSheetsForStyleSheetList.swap(other.m_styleSheetsForStyleSheetList); 53 m_activeAuthorStyleSheets.swap(other.m_activeAuthorStyleSheets); 54 } 55 56 void StyleSheetCollectionBase::appendActiveStyleSheets(const Vector<RefPtr<CSSStyleSheet> >& sheets) 57 { 58 m_activeAuthorStyleSheets.append(sheets); 59 } 60 61 void StyleSheetCollectionBase::appendActiveStyleSheet(CSSStyleSheet* sheet) 62 { 63 m_activeAuthorStyleSheets.append(sheet); 64 } 65 66 void StyleSheetCollectionBase::appendSheetForList(StyleSheet* sheet) 67 { 68 m_styleSheetsForStyleSheetList.append(sheet); 69 } 70 71 72 StyleSheetCollection::StyleSheetCollection(TreeScope& treeScope) 73 : m_treeScope(treeScope) 74 , m_hadActiveLoadingStylesheet(false) 75 , m_usesRemUnits(false) 76 { 77 } 78 79 void StyleSheetCollection::addStyleSheetCandidateNode(Node* node, bool createdByParser) 80 { 81 if (!node->inDocument()) 82 return; 83 84 // Until the <body> exists, we have no choice but to compare document positions, 85 // since styles outside of the body and head continue to be shunted into the head 86 // (and thus can shift to end up before dynamically added DOM content that is also 87 // outside the body). 88 if (createdByParser && document()->body()) 89 m_styleSheetCandidateNodes.parserAdd(node); 90 else 91 m_styleSheetCandidateNodes.add(node); 92 93 if (!isHTMLStyleElement(node)) 94 return; 95 96 ContainerNode* scopingNode = toHTMLStyleElement(node)->scopingNode(); 97 if (!isTreeScopeRoot(scopingNode)) 98 m_scopingNodesForStyleScoped.add(scopingNode); 99 } 100 101 void StyleSheetCollection::removeStyleSheetCandidateNode(Node* node, ContainerNode* scopingNode) 102 { 103 m_styleSheetCandidateNodes.remove(node); 104 105 if (!isTreeScopeRoot(scopingNode)) 106 m_scopingNodesForStyleScoped.remove(scopingNode); 107 } 108 109 StyleSheetCollection::StyleResolverUpdateType StyleSheetCollection::compareStyleSheets(const Vector<RefPtr<CSSStyleSheet> >& oldStyleSheets, const Vector<RefPtr<CSSStyleSheet> >& newStylesheets, Vector<StyleSheetContents*>& addedSheets) 110 { 111 unsigned newStyleSheetCount = newStylesheets.size(); 112 unsigned oldStyleSheetCount = oldStyleSheets.size(); 113 ASSERT(newStyleSheetCount >= oldStyleSheetCount); 114 115 if (!newStyleSheetCount) 116 return Reconstruct; 117 118 unsigned newIndex = 0; 119 for (unsigned oldIndex = 0; oldIndex < oldStyleSheetCount; ++oldIndex) { 120 while (oldStyleSheets[oldIndex] != newStylesheets[newIndex]) { 121 addedSheets.append(newStylesheets[newIndex]->contents()); 122 if (++newIndex == newStyleSheetCount) 123 return Reconstruct; 124 } 125 if (++newIndex == newStyleSheetCount) 126 return Reconstruct; 127 } 128 bool hasInsertions = !addedSheets.isEmpty(); 129 while (newIndex < newStyleSheetCount) { 130 addedSheets.append(newStylesheets[newIndex]->contents()); 131 ++newIndex; 132 } 133 // If all new sheets were added at the end of the list we can just add them to existing StyleResolver. 134 // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly. 135 return hasInsertions ? Reset : Additive; 136 } 137 138 bool StyleSheetCollection::activeLoadingStyleSheetLoaded(const Vector<RefPtr<CSSStyleSheet> >& newStyleSheets) 139 { 140 // StyleSheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done. 141 bool hasActiveLoadingStylesheet = false; 142 unsigned newStylesheetCount = newStyleSheets.size(); 143 for (unsigned i = 0; i < newStylesheetCount; ++i) { 144 if (newStyleSheets[i]->isLoading()) 145 hasActiveLoadingStylesheet = true; 146 } 147 if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) { 148 m_hadActiveLoadingStylesheet = false; 149 return true; 150 } 151 m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet; 152 return false; 153 } 154 155 static bool styleSheetContentsHasFontFaceRule(Vector<StyleSheetContents*> sheets) 156 { 157 for (unsigned i = 0; i < sheets.size(); ++i) { 158 ASSERT(sheets[i]); 159 if (sheets[i]->hasFontFaceRule()) 160 return true; 161 } 162 return false; 163 } 164 165 static bool cssStyleSheetHasFontFaceRule(const Vector<RefPtr<CSSStyleSheet> > sheets) 166 { 167 for (unsigned i = 0; i < sheets.size(); ++i) { 168 ASSERT(sheets[i]); 169 if (sheets[i]->contents()->hasFontFaceRule()) 170 return true; 171 } 172 return false; 173 } 174 175 void StyleSheetCollection::analyzeStyleSheetChange(StyleResolverUpdateMode updateMode, const StyleSheetCollectionBase& newCollection, StyleSheetChange& change) 176 { 177 if (activeLoadingStyleSheetLoaded(newCollection.activeAuthorStyleSheets())) 178 return; 179 180 if (updateMode != AnalyzedStyleUpdate) 181 return; 182 183 // Find out which stylesheets are new. 184 Vector<StyleSheetContents*> addedSheets; 185 if (m_activeAuthorStyleSheets.size() <= newCollection.activeAuthorStyleSheets().size()) { 186 change.styleResolverUpdateType = compareStyleSheets(m_activeAuthorStyleSheets, newCollection.activeAuthorStyleSheets(), addedSheets); 187 } else { 188 StyleResolverUpdateType updateType = compareStyleSheets(newCollection.activeAuthorStyleSheets(), m_activeAuthorStyleSheets, addedSheets); 189 if (updateType != Additive) { 190 change.styleResolverUpdateType = updateType; 191 } else { 192 if (styleSheetContentsHasFontFaceRule(addedSheets)) { 193 change.styleResolverUpdateType = ResetStyleResolverAndFontSelector; 194 return; 195 } 196 // FIXME: since currently all stylesheets are re-added after reseting styleresolver, 197 // fontSelector should be always reset. After creating RuleSet for each StyleSheetContents, 198 // we can avoid appending all stylesheetcontents in reset case. 199 // So we can remove "styleSheetContentsHasFontFaceRule(newSheets)". 200 if (cssStyleSheetHasFontFaceRule(newCollection.activeAuthorStyleSheets())) 201 change.styleResolverUpdateType = ResetStyleResolverAndFontSelector; 202 else 203 change.styleResolverUpdateType = Reset; 204 } 205 } 206 207 // FIXME: If styleResolverUpdateType is Reconstruct, we should return early here since 208 // we need to recalc the whole document. It's wrong to use StyleInvalidationAnalysis since 209 // it only looks at the addedSheets. 210 211 // No point in doing the analysis work if we're just going to recalc the whole document anyways. 212 // This needs to be done after the compareStyleSheets calls above to ensure we don't throw away 213 // the StyleResolver if we don't need to. 214 if (document()->hasPendingForcedStyleRecalc()) 215 return; 216 217 // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs. 218 if (!document()->body() || document()->hasNodesWithPlaceholderStyle()) 219 return; 220 StyleInvalidationAnalysis invalidationAnalysis(addedSheets); 221 if (invalidationAnalysis.dirtiesAllStyle()) 222 return; 223 invalidationAnalysis.invalidateStyle(*document()); 224 change.requiresFullStyleRecalc = false; 225 return; 226 } 227 228 void StyleSheetCollection::clearMediaQueryRuleSetStyleSheets() 229 { 230 for (size_t i = 0; i < m_activeAuthorStyleSheets.size(); ++i) { 231 StyleSheetContents* contents = m_activeAuthorStyleSheets[i]->contents(); 232 if (contents->hasMediaQueries()) 233 contents->clearRuleSet(); 234 } 235 } 236 237 void StyleSheetCollection::resetAllRuleSetsInTreeScope(StyleResolver* styleResolver) 238 { 239 // FIXME: If many web developers use style scoped, implement reset RuleSets in per-scoping node manner. 240 if (DocumentOrderedList* styleScopedScopingNodes = scopingNodesForStyleScoped()) { 241 for (DocumentOrderedList::iterator it = styleScopedScopingNodes->begin(); it != styleScopedScopingNodes->end(); ++it) 242 styleResolver->resetAuthorStyle(toContainerNode(*it)); 243 } 244 if (ListHashSet<Node*, 4>* removedNodes = scopingNodesRemoved()) { 245 for (ListHashSet<Node*, 4>::iterator it = removedNodes->begin(); it != removedNodes->end(); ++it) 246 styleResolver->resetAuthorStyle(toContainerNode(*it)); 247 } 248 styleResolver->resetAuthorStyle(toContainerNode(m_treeScope.rootNode())); 249 } 250 251 static bool styleSheetsUseRemUnits(const Vector<RefPtr<CSSStyleSheet> >& sheets) 252 { 253 for (unsigned i = 0; i < sheets.size(); ++i) { 254 if (sheets[i]->contents()->usesRemUnits()) 255 return true; 256 } 257 return false; 258 } 259 260 void StyleSheetCollection::updateUsesRemUnits() 261 { 262 m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets); 263 } 264 265 } 266