1/* 2 * Copyright (C) 2012 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "StyleInvalidationAnalysis.h" 28 29#include "CSSSelectorList.h" 30#include "Document.h" 31#include "NodeTraversal.h" 32#include "StyleRuleImport.h" 33#include "StyleSheetContents.h" 34#include "StyledElement.h" 35 36namespace WebCore { 37 38StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector<StyleSheetContents*>& sheets) 39 : m_dirtiesAllStyle(false) 40{ 41 for (unsigned i = 0; i < sheets.size() && !m_dirtiesAllStyle; ++i) 42 analyzeStyleSheet(sheets[i]); 43} 44 45static bool determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes) 46{ 47 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) { 48 const CSSSelector* scopeSelector = 0; 49 // This picks the widest scope, not the narrowest, to minimize the number of found scopes. 50 for (const CSSSelector* current = selector; current; current = current->tagHistory()) { 51 // Prefer ids over classes. 52 if (current->m_match == CSSSelector::Id) 53 scopeSelector = current; 54 else if (current->m_match == CSSSelector::Class && (!scopeSelector || scopeSelector->m_match != CSSSelector::Id)) 55 scopeSelector = current; 56 CSSSelector::Relation relation = current->relation(); 57 if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector) 58 break; 59 } 60 if (!scopeSelector) 61 return false; 62 ASSERT(scopeSelector->m_match == CSSSelector::Class || scopeSelector->m_match == CSSSelector::Id); 63 if (scopeSelector->m_match == CSSSelector::Id) 64 idScopes.add(scopeSelector->value().impl()); 65 else 66 classScopes.add(scopeSelector->value().impl()); 67 } 68 return true; 69} 70 71void StyleInvalidationAnalysis::analyzeStyleSheet(StyleSheetContents* styleSheetContents) 72{ 73 ASSERT(!styleSheetContents->isLoading()); 74 75 // See if all rules on the sheet are scoped to some specific ids or classes. 76 // Then test if we actually have any of those in the tree at the moment. 77 const Vector<RefPtr<StyleRuleImport> >& importRules = styleSheetContents->importRules(); 78 for (unsigned i = 0; i < importRules.size(); ++i) { 79 if (!importRules[i]->styleSheet()) 80 continue; 81 analyzeStyleSheet(importRules[i]->styleSheet()); 82 if (m_dirtiesAllStyle) 83 return; 84 } 85 const Vector<RefPtr<StyleRuleBase> >& rules = styleSheetContents->childRules(); 86 for (unsigned i = 0; i < rules.size(); i++) { 87 StyleRuleBase* rule = rules[i].get(); 88 if (!rule->isStyleRule()) { 89 // FIXME: Media rules and maybe some others could be allowed. 90 m_dirtiesAllStyle = true; 91 return; 92 } 93 StyleRule* styleRule = static_cast<StyleRule*>(rule); 94 if (!determineSelectorScopes(styleRule->selectorList(), m_idScopes, m_classScopes)) { 95 m_dirtiesAllStyle = true; 96 return; 97 } 98 } 99} 100 101static bool elementMatchesSelectorScopes(const Element* element, const HashSet<AtomicStringImpl*>& idScopes, const HashSet<AtomicStringImpl*>& classScopes) 102{ 103 if (!idScopes.isEmpty() && element->hasID() && idScopes.contains(element->idForStyleResolution().impl())) 104 return true; 105 if (classScopes.isEmpty() || !element->hasClass()) 106 return false; 107 const SpaceSplitString& classNames = element->classNames(); 108 for (unsigned i = 0; i < classNames.size(); ++i) { 109 if (classScopes.contains(classNames[i].impl())) 110 return true; 111 } 112 return false; 113} 114 115void StyleInvalidationAnalysis::invalidateStyle(Document* document) 116{ 117 ASSERT(!m_dirtiesAllStyle); 118 if (m_idScopes.isEmpty() && m_classScopes.isEmpty()) 119 return; 120 Element* element = ElementTraversal::firstWithin(document); 121 while (element) { 122 if (elementMatchesSelectorScopes(element, m_idScopes, m_classScopes)) { 123 element->setNeedsStyleRecalc(); 124 // The whole subtree is now invalidated, we can skip to the next sibling. 125 element = ElementTraversal::nextSkippingChildren(element); 126 continue; 127 } 128 element = ElementTraversal::next(element); 129 } 130} 131 132} 133