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/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 blink { 41 42 TreeScopeStyleSheetCollection::TreeScopeStyleSheetCollection(TreeScope& treeScope) 43 : m_treeScope(treeScope) 44 , m_hadActiveLoadingStylesheet(false) 45 , m_usesRemUnits(false) 46 { 47 } 48 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 64 void TreeScopeStyleSheetCollection::removeStyleSheetCandidateNode(Node* node, ContainerNode* scopingNode) 65 { 66 m_styleSheetCandidateNodes.remove(node); 67 } 68 69 TreeScopeStyleSheetCollection::StyleResolverUpdateType TreeScopeStyleSheetCollection::compareStyleSheets(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& oldStyleSheets, const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStylesheets, WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& addedSheets) 70 { 71 unsigned newStyleSheetCount = newStylesheets.size(); 72 unsigned oldStyleSheetCount = oldStyleSheets.size(); 73 ASSERT(newStyleSheetCount >= oldStyleSheetCount); 74 75 if (!newStyleSheetCount) 76 return Reconstruct; 77 78 unsigned newIndex = 0; 79 for (unsigned oldIndex = 0; oldIndex < oldStyleSheetCount; ++oldIndex) { 80 while (oldStyleSheets[oldIndex] != newStylesheets[newIndex]) { 81 addedSheets.append(newStylesheets[newIndex]->contents()); 82 if (++newIndex == newStyleSheetCount) 83 return Reconstruct; 84 } 85 if (++newIndex == newStyleSheetCount) 86 return Reconstruct; 87 } 88 bool hasInsertions = !addedSheets.isEmpty(); 89 while (newIndex < newStyleSheetCount) { 90 addedSheets.append(newStylesheets[newIndex]->contents()); 91 ++newIndex; 92 } 93 // If all new sheets were added at the end of the list we can just add them to existing StyleResolver. 94 // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly. 95 return hasInsertions ? Reset : Additive; 96 } 97 98 bool TreeScopeStyleSheetCollection::activeLoadingStyleSheetLoaded(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStyleSheets) 99 { 100 // StyleSheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done. 101 bool hasActiveLoadingStylesheet = false; 102 unsigned newStylesheetCount = newStyleSheets.size(); 103 for (unsigned i = 0; i < newStylesheetCount; ++i) { 104 if (newStyleSheets[i]->isLoading()) 105 hasActiveLoadingStylesheet = true; 106 } 107 if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) { 108 m_hadActiveLoadingStylesheet = false; 109 return true; 110 } 111 m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet; 112 return false; 113 } 114 115 static bool findFontFaceRulesFromStyleSheetContents(const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& sheets, WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >& fontFaceRules) 116 { 117 bool hasFontFaceRule = false; 118 119 for (unsigned i = 0; i < sheets.size(); ++i) { 120 ASSERT(sheets[i]); 121 if (sheets[i]->hasFontFaceRule()) { 122 // FIXME: We don't need this for styles in shadow tree. 123 sheets[i]->findFontFaceRules(fontFaceRules); 124 hasFontFaceRule = true; 125 } 126 } 127 return hasFontFaceRule; 128 } 129 130 void TreeScopeStyleSheetCollection::analyzeStyleSheetChange(StyleResolverUpdateMode updateMode, const StyleSheetCollection& newCollection, StyleSheetChange& change) 131 { 132 if (activeLoadingStyleSheetLoaded(newCollection.activeAuthorStyleSheets())) 133 return; 134 135 if (updateMode != AnalyzedStyleUpdate) 136 return; 137 138 // Find out which stylesheets are new. 139 WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> > addedSheets; 140 if (m_activeAuthorStyleSheets.size() <= newCollection.activeAuthorStyleSheets().size()) { 141 change.styleResolverUpdateType = compareStyleSheets(m_activeAuthorStyleSheets, newCollection.activeAuthorStyleSheets(), addedSheets); 142 } else { 143 StyleResolverUpdateType updateType = compareStyleSheets(newCollection.activeAuthorStyleSheets(), m_activeAuthorStyleSheets, addedSheets); 144 if (updateType != Additive) { 145 change.styleResolverUpdateType = updateType; 146 } else { 147 change.styleResolverUpdateType = Reset; 148 // If @font-face is removed, needs full style recalc. 149 if (findFontFaceRulesFromStyleSheetContents(addedSheets, change.fontFaceRulesToRemove)) 150 return; 151 } 152 } 153 154 // FIXME: If styleResolverUpdateType is Reconstruct, we should return early here since 155 // we need to recalc the whole document. It's wrong to use StyleSheetInvalidationAnalysis since 156 // it only looks at the addedSheets. 157 158 // No point in doing the analysis work if we're just going to recalc the whole document anyways. 159 // This needs to be done after the compareStyleSheets calls above to ensure we don't throw away 160 // the StyleResolver if we don't need to. 161 if (document().hasPendingForcedStyleRecalc()) 162 return; 163 164 // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs. 165 if (!document().body() || document().hasNodesWithPlaceholderStyle()) 166 return; 167 StyleSheetInvalidationAnalysis invalidationAnalysis(addedSheets); 168 if (invalidationAnalysis.dirtiesAllStyle()) 169 return; 170 invalidationAnalysis.invalidateStyle(document()); 171 change.requiresFullStyleRecalc = false; 172 return; 173 } 174 175 void TreeScopeStyleSheetCollection::clearMediaQueryRuleSetStyleSheets() 176 { 177 for (size_t i = 0; i < m_activeAuthorStyleSheets.size(); ++i) { 178 StyleSheetContents* contents = m_activeAuthorStyleSheets[i]->contents(); 179 if (contents->hasMediaQueries()) 180 contents->clearRuleSet(); 181 } 182 } 183 184 void TreeScopeStyleSheetCollection::enableExitTransitionStylesheets() 185 { 186 DocumentOrderedList::iterator begin = m_styleSheetCandidateNodes.begin(); 187 DocumentOrderedList::iterator end = m_styleSheetCandidateNodes.end(); 188 for (DocumentOrderedList::iterator it = begin; it != end; ++it) { 189 Node* node = *it; 190 if (isHTMLLinkElement(*node)) 191 toHTMLLinkElement(node)->enableIfExitTransitionStyle(); 192 } 193 } 194 195 static bool styleSheetsUseRemUnits(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& sheets) 196 { 197 for (unsigned i = 0; i < sheets.size(); ++i) { 198 if (sheets[i]->contents()->usesRemUnits()) 199 return true; 200 } 201 return false; 202 } 203 204 void TreeScopeStyleSheetCollection::updateUsesRemUnits() 205 { 206 m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets); 207 } 208 209 void TreeScopeStyleSheetCollection::trace(Visitor* visitor) 210 { 211 visitor->trace(m_treeScope); 212 visitor->trace(m_styleSheetCandidateNodes); 213 StyleSheetCollection::trace(visitor); 214 } 215 216 } 217