StyleInvalidationAnalysis.cpp [plain text]
#include "config.h"
#include "StyleInvalidationAnalysis.h"
#include "CSSSelectorList.h"
#include "Document.h"
#include "NodeTraversal.h"
#include "StyleRuleImport.h"
#include "StyleSheetContents.h"
#include "StyledElement.h"
namespace WebCore {
StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector<StyleSheetContents*>& sheets)
: m_dirtiesAllStyle(false)
{
for (unsigned i = 0; i < sheets.size() && !m_dirtiesAllStyle; ++i)
analyzeStyleSheet(sheets[i]);
}
static bool determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
{
for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
const CSSSelector* scopeSelector = 0;
for (const CSSSelector* current = selector; current; current = current->tagHistory()) {
if (current->m_match == CSSSelector::Id)
scopeSelector = current;
else if (current->m_match == CSSSelector::Class && (!scopeSelector || scopeSelector->m_match != CSSSelector::Id))
scopeSelector = current;
CSSSelector::Relation relation = current->relation();
if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector)
break;
}
if (!scopeSelector)
return false;
ASSERT(scopeSelector->m_match == CSSSelector::Class || scopeSelector->m_match == CSSSelector::Id);
if (scopeSelector->m_match == CSSSelector::Id)
idScopes.add(scopeSelector->value().impl());
else
classScopes.add(scopeSelector->value().impl());
}
return true;
}
void StyleInvalidationAnalysis::analyzeStyleSheet(StyleSheetContents* styleSheetContents)
{
ASSERT(!styleSheetContents->isLoading());
const Vector<RefPtr<StyleRuleImport> >& importRules = styleSheetContents->importRules();
for (unsigned i = 0; i < importRules.size(); ++i) {
if (!importRules[i]->styleSheet())
continue;
analyzeStyleSheet(importRules[i]->styleSheet());
if (m_dirtiesAllStyle)
return;
}
const Vector<RefPtr<StyleRuleBase> >& rules = styleSheetContents->childRules();
for (unsigned i = 0; i < rules.size(); i++) {
StyleRuleBase* rule = rules[i].get();
if (!rule->isStyleRule()) {
m_dirtiesAllStyle = true;
return;
}
StyleRule* styleRule = static_cast<StyleRule*>(rule);
if (!determineSelectorScopes(styleRule->selectorList(), m_idScopes, m_classScopes)) {
m_dirtiesAllStyle = true;
return;
}
}
}
static bool elementMatchesSelectorScopes(const Element* element, const HashSet<AtomicStringImpl*>& idScopes, const HashSet<AtomicStringImpl*>& classScopes)
{
if (!idScopes.isEmpty() && element->hasID() && idScopes.contains(element->idForStyleResolution().impl()))
return true;
if (classScopes.isEmpty() || !element->hasClass())
return false;
const SpaceSplitString& classNames = element->classNames();
for (unsigned i = 0; i < classNames.size(); ++i) {
if (classScopes.contains(classNames[i].impl()))
return true;
}
return false;
}
void StyleInvalidationAnalysis::invalidateStyle(Document* document)
{
ASSERT(!m_dirtiesAllStyle);
if (m_idScopes.isEmpty() && m_classScopes.isEmpty())
return;
Element* element = ElementTraversal::firstWithin(document);
while (element) {
if (elementMatchesSelectorScopes(element, m_idScopes, m_classScopes)) {
element->setNeedsStyleRecalc();
element = ElementTraversal::nextSkippingChildren(element);
continue;
}
element = ElementTraversal::next(element);
}
}
}