blob: 77ec1e4392ac0003df9dd3e0c55d6ca2ab2aaa32 [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* (C) 2006 Alexey Proskuryakov (ap@webkit.org)
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "core/dom/StyleEngine.h"
#include "core/HTMLNames.h"
#include "core/css/CSSDefaultStyleSheets.h"
#include "core/css/CSSFontSelector.h"
#include "core/css/CSSStyleSheet.h"
#include "core/css/FontFaceCache.h"
#include "core/css/StyleSheetContents.h"
#include "core/css/invalidation/InvalidationSet.h"
#include "core/css/resolver/ScopedStyleResolver.h"
#include "core/dom/DocumentStyleSheetCollector.h"
#include "core/dom/Element.h"
#include "core/dom/ProcessingInstruction.h"
#include "core/dom/ShadowTreeStyleSheetCollection.h"
#include "core/dom/StyleChangeReason.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/frame/Settings.h"
#include "core/html/HTMLIFrameElement.h"
#include "core/html/HTMLLinkElement.h"
#include "core/html/imports/HTMLImportsController.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/page/Page.h"
#include "core/svg/SVGStyleElement.h"
#include "platform/TraceEvent.h"
#include "platform/fonts/FontCache.h"
namespace blink {
using namespace HTMLNames;
StyleEngine::StyleEngine(Document& document)
: m_document(&document)
, m_isMaster(!document.importsController() || document.importsController()->master() == &document)
, m_documentStyleSheetCollection(DocumentStyleSheetCollection::create(document))
// We don't need to create CSSFontSelector for imported document or
// HTMLTemplateElement's document, because those documents have no frame.
, m_fontSelector(document.frame() ? CSSFontSelector::create(&document) : nullptr)
{
if (m_fontSelector)
m_fontSelector->registerForInvalidationCallbacks(this);
}
StyleEngine::~StyleEngine()
{
}
static bool isStyleElement(Node& node)
{
return isHTMLStyleElement(node) || isSVGStyleElement(node);
}
inline Document* StyleEngine::master()
{
if (isMaster())
return m_document;
HTMLImportsController* import = document().importsController();
if (!import) // Document::import() can return null while executing its destructor.
return 0;
return import->master();
}
TreeScopeStyleSheetCollection* StyleEngine::ensureStyleSheetCollectionFor(TreeScope& treeScope)
{
if (treeScope == m_document)
return documentStyleSheetCollection();
StyleSheetCollectionMap::AddResult result = m_styleSheetCollectionMap.add(&treeScope, nullptr);
if (result.isNewEntry)
result.storedValue->value = new ShadowTreeStyleSheetCollection(toShadowRoot(treeScope));
return result.storedValue->value.get();
}
TreeScopeStyleSheetCollection* StyleEngine::styleSheetCollectionFor(TreeScope& treeScope)
{
if (treeScope == m_document)
return documentStyleSheetCollection();
StyleSheetCollectionMap::iterator it = m_styleSheetCollectionMap.find(&treeScope);
if (it == m_styleSheetCollectionMap.end())
return 0;
return it->value.get();
}
const HeapVector<Member<StyleSheet>>& StyleEngine::styleSheetsForStyleSheetList(TreeScope& treeScope)
{
if (treeScope == m_document)
return documentStyleSheetCollection()->styleSheetsForStyleSheetList();
return ensureStyleSheetCollectionFor(treeScope)->styleSheetsForStyleSheetList();
}
void StyleEngine::resetCSSFeatureFlags(const RuleFeatureSet& features)
{
m_usesSiblingRules = features.usesSiblingRules();
m_usesFirstLineRules = features.usesFirstLineRules();
m_usesWindowInactiveSelector = features.usesWindowInactiveSelector();
m_maxDirectAdjacentSelectors = features.maxDirectAdjacentSelectors();
}
void StyleEngine::injectAuthorSheet(StyleSheetContents* authorSheet)
{
m_injectedAuthorStyleSheets.append(CSSStyleSheet::create(authorSheet, m_document));
markDocumentDirty();
resolverChanged(AnalyzedStyleUpdate);
}
void StyleEngine::addPendingSheet(StyleEngineContext &context)
{
m_pendingScriptBlockingStylesheets++;
context.addingPendingSheet(document());
if (context.addedPendingSheetBeforeBody())
m_pendingRenderBlockingStylesheets++;
}
// This method is called whenever a top-level stylesheet has finished loading.
void StyleEngine::removePendingSheet(Node* styleSheetCandidateNode, const StyleEngineContext &context)
{
DCHECK(styleSheetCandidateNode);
TreeScope* treeScope = isStyleElement(*styleSheetCandidateNode) ? &styleSheetCandidateNode->treeScope() : m_document.get();
if (styleSheetCandidateNode->inShadowIncludingDocument())
markTreeScopeDirty(*treeScope);
if (context.addedPendingSheetBeforeBody()) {
DCHECK_GT(m_pendingRenderBlockingStylesheets, 0);
m_pendingRenderBlockingStylesheets--;
}
// Make sure we knew this sheet was pending, and that our count isn't out of sync.
DCHECK_GT(m_pendingScriptBlockingStylesheets, 0);
m_pendingScriptBlockingStylesheets--;
if (m_pendingScriptBlockingStylesheets)
return;
document().didRemoveAllPendingStylesheet();
}
void StyleEngine::setNeedsActiveStyleUpdate(StyleSheet* sheet, StyleResolverUpdateMode updateMode)
{
// resolverChanged() is called for inactive non-master documents because
// import documents are inactive documents. resolverChanged() for imports
// will call resolverChanged() for the master document and update the active
// stylesheets including the ones from the import.
if (!document().isActive() && isMaster())
return;
if (sheet && document().isActive()) {
Node* node = sheet->ownerNode();
if (node && node->inShadowIncludingDocument()) {
TreeScope& treeScope = isStyleElement(*node) ? node->treeScope() : *m_document;
DCHECK(isStyleElement(*node) || node->treeScope() == m_document);
markTreeScopeDirty(treeScope);
}
}
resolverChanged(updateMode);
}
void StyleEngine::addStyleSheetCandidateNode(Node* node)
{
if (!node->inShadowIncludingDocument() || document().isDetached())
return;
TreeScope& treeScope = isStyleElement(*node) ? node->treeScope() : *m_document;
DCHECK(isStyleElement(*node) || treeScope == m_document);
DCHECK(!isXSLStyleSheet(*node));
TreeScopeStyleSheetCollection* collection = ensureStyleSheetCollectionFor(treeScope);
DCHECK(collection);
collection->addStyleSheetCandidateNode(node);
markTreeScopeDirty(treeScope);
if (treeScope != m_document)
m_activeTreeScopes.add(&treeScope);
}
void StyleEngine::removeStyleSheetCandidateNode(Node* node)
{
removeStyleSheetCandidateNode(node, *m_document);
}
void StyleEngine::removeStyleSheetCandidateNode(Node* node, TreeScope& treeScope)
{
DCHECK(isStyleElement(*node) || treeScope == m_document);
DCHECK(!isXSLStyleSheet(*node));
TreeScopeStyleSheetCollection* collection = styleSheetCollectionFor(treeScope);
// After detaching document, collection could be null. In the case,
// we should not update anything. Instead, just return.
if (!collection)
return;
collection->removeStyleSheetCandidateNode(node);
markTreeScopeDirty(treeScope);
}
void StyleEngine::modifiedStyleSheetCandidateNode(Node* node)
{
if (!node->inShadowIncludingDocument())
return;
TreeScope& treeScope = isStyleElement(*node) ? node->treeScope() : *m_document;
DCHECK(isStyleElement(*node) || treeScope == m_document);
markTreeScopeDirty(treeScope);
resolverChanged(FullStyleUpdate);
}
void StyleEngine::watchedSelectorsChanged()
{
if (m_resolver) {
m_resolver->initWatchedSelectorRules();
m_resolver->resetRuleFeatures();
}
document().setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::DeclarativeContent));
}
bool StyleEngine::shouldUpdateDocumentStyleSheetCollection(StyleResolverUpdateMode updateMode) const
{
return m_documentScopeDirty || updateMode == FullStyleUpdate;
}
bool StyleEngine::shouldUpdateShadowTreeStyleSheetCollection(StyleResolverUpdateMode updateMode) const
{
return !m_dirtyTreeScopes.isEmpty() || updateMode == FullStyleUpdate;
}
void StyleEngine::clearMediaQueryRuleSetOnTreeScopeStyleSheets(UnorderedTreeScopeSet& treeScopes)
{
for (TreeScope* treeScope : treeScopes) {
DCHECK(treeScope != m_document);
ShadowTreeStyleSheetCollection* collection = toShadowTreeStyleSheetCollection(styleSheetCollectionFor(*treeScope));
DCHECK(collection);
collection->clearMediaQueryRuleSetStyleSheets();
}
}
void StyleEngine::clearMediaQueryRuleSetStyleSheets()
{
resolverChanged(FullStyleUpdate);
documentStyleSheetCollection()->clearMediaQueryRuleSetStyleSheets();
clearMediaQueryRuleSetOnTreeScopeStyleSheets(m_activeTreeScopes);
}
void StyleEngine::updateStyleSheetsInImport(DocumentStyleSheetCollector& parentCollector)
{
DCHECK(!isMaster());
HeapVector<Member<StyleSheet>> sheetsForList;
ImportedDocumentStyleSheetCollector subcollector(parentCollector, sheetsForList);
documentStyleSheetCollection()->collectStyleSheets(*this, subcollector);
documentStyleSheetCollection()->swapSheetsForSheetList(sheetsForList);
}
void StyleEngine::updateActiveStyleSheetsInShadow(StyleResolverUpdateMode updateMode, TreeScope* treeScope, UnorderedTreeScopeSet& treeScopesRemoved)
{
DCHECK_NE(treeScope, m_document);
ShadowTreeStyleSheetCollection* collection = toShadowTreeStyleSheetCollection(styleSheetCollectionFor(*treeScope));
DCHECK(collection);
collection->updateActiveStyleSheets(*this, updateMode);
if (!collection->hasStyleSheetCandidateNodes()) {
treeScopesRemoved.add(treeScope);
// When removing TreeScope from ActiveTreeScopes,
// its resolver should be destroyed by invoking resetAuthorStyle.
DCHECK(!treeScope->scopedStyleResolver());
}
}
void StyleEngine::updateActiveStyleSheets(StyleResolverUpdateMode updateMode)
{
DCHECK(isMaster());
DCHECK(!document().inStyleRecalc());
if (!document().isActive())
return;
TRACE_EVENT0("blink,blink_style", "StyleEngine::updateActiveStyleSheets");
if (shouldUpdateDocumentStyleSheetCollection(updateMode))
documentStyleSheetCollection()->updateActiveStyleSheets(*this, updateMode);
if (shouldUpdateShadowTreeStyleSheetCollection(updateMode)) {
UnorderedTreeScopeSet treeScopesRemoved;
if (updateMode == FullStyleUpdate) {
for (TreeScope* treeScope : m_activeTreeScopes)
updateActiveStyleSheetsInShadow(updateMode, treeScope, treeScopesRemoved);
} else {
for (TreeScope* treeScope : m_dirtyTreeScopes)
updateActiveStyleSheetsInShadow(updateMode, treeScope, treeScopesRemoved);
}
for (TreeScope* treeScope : treeScopesRemoved)
m_activeTreeScopes.remove(treeScope);
}
InspectorInstrumentation::activeStyleSheetsUpdated(m_document);
m_dirtyTreeScopes.clear();
m_documentScopeDirty = false;
}
const HeapVector<Member<CSSStyleSheet>> StyleEngine::activeStyleSheetsForInspector() const
{
if (m_activeTreeScopes.isEmpty())
return documentStyleSheetCollection()->activeAuthorStyleSheets();
HeapVector<Member<CSSStyleSheet>> activeStyleSheets;
activeStyleSheets.appendVector(documentStyleSheetCollection()->activeAuthorStyleSheets());
for (TreeScope* treeScope : m_activeTreeScopes) {
if (TreeScopeStyleSheetCollection* collection = m_styleSheetCollectionMap.get(treeScope))
activeStyleSheets.appendVector(collection->activeAuthorStyleSheets());
}
// FIXME: Inspector needs a vector which has all active stylesheets.
// However, creating such a large vector might cause performance regression.
// Need to implement some smarter solution.
return activeStyleSheets;
}
void StyleEngine::didRemoveShadowRoot(ShadowRoot* shadowRoot)
{
m_styleSheetCollectionMap.remove(shadowRoot);
m_activeTreeScopes.remove(shadowRoot);
m_dirtyTreeScopes.remove(shadowRoot);
}
void StyleEngine::shadowRootRemovedFromDocument(ShadowRoot* shadowRoot)
{
if (StyleResolver* styleResolver = resolver()) {
styleResolver->resetAuthorStyle(*shadowRoot);
if (TreeScopeStyleSheetCollection* collection = styleSheetCollectionFor(*shadowRoot))
styleResolver->removePendingAuthorStyleSheets(collection->activeAuthorStyleSheets());
}
m_styleSheetCollectionMap.remove(shadowRoot);
m_activeTreeScopes.remove(shadowRoot);
m_dirtyTreeScopes.remove(shadowRoot);
}
void StyleEngine::appendActiveAuthorStyleSheets()
{
DCHECK(isMaster());
m_resolver->appendAuthorStyleSheets(documentStyleSheetCollection()->activeAuthorStyleSheets());
for (TreeScope* treeScope : m_activeTreeScopes) {
if (TreeScopeStyleSheetCollection* collection = m_styleSheetCollectionMap.get(treeScope))
m_resolver->appendAuthorStyleSheets(collection->activeAuthorStyleSheets());
}
m_resolver->finishAppendAuthorStyleSheets();
}
void StyleEngine::createResolver()
{
TRACE_EVENT1("blink", "StyleEngine::createResolver", "frame", document().frame());
// It is a programming error to attempt to resolve style on a Document
// which is not in a frame. Code which hits this should have checked
// Document::isActive() before calling into code which could get here.
DCHECK(document().frame());
m_resolver = StyleResolver::create(*m_document);
// A scoped style resolver for document will be created during
// appendActiveAuthorStyleSheets if needed.
appendActiveAuthorStyleSheets();
}
void StyleEngine::clearResolver()
{
DCHECK(!document().inStyleRecalc());
DCHECK(isMaster() || !m_resolver);
document().clearScopedStyleResolver();
// StyleEngine::shadowRootRemovedFromDocument removes not-in-document
// treescopes from activeTreeScopes. StyleEngine::didRemoveShadowRoot
// removes treescopes which are being destroyed from activeTreeScopes.
// So we need to clearScopedStyleResolver for treescopes which have been
// just removed from document. If document is destroyed before invoking
// updateActiveStyleSheets, the treescope has a scopedStyleResolver which
// has destroyed StyleSheetContents.
for (TreeScope* treeScope : m_activeTreeScopes)
treeScope->clearScopedStyleResolver();
if (m_resolver) {
TRACE_EVENT1("blink", "StyleEngine::clearResolver", "frame", document().frame());
m_resolver->dispose();
m_resolver.clear();
}
}
void StyleEngine::clearMasterResolver()
{
if (Document* master = this->master())
master->styleEngine().clearResolver();
}
void StyleEngine::didDetach()
{
clearResolver();
}
bool StyleEngine::shouldClearResolver() const
{
return !m_didCalculateResolver && !haveScriptBlockingStylesheetsLoaded();
}
void StyleEngine::resolverChanged(StyleResolverUpdateMode mode)
{
if (!isMaster()) {
if (Document* master = this->master())
master->styleEngine().resolverChanged(mode);
return;
}
// Don't bother updating, since we haven't loaded all our style info yet
// and haven't calculated the style selector for the first time.
if (!document().isActive() || shouldClearResolver()) {
clearResolver();
return;
}
m_didCalculateResolver = true;
updateActiveStyleSheets(mode);
}
void StyleEngine::clearFontCache()
{
if (m_fontSelector)
m_fontSelector->fontFaceCache()->clearCSSConnected();
if (m_resolver)
m_resolver->invalidateMatchedPropertiesCache();
}
void StyleEngine::updateGenericFontFamilySettings()
{
// FIXME: we should not update generic font family settings when
// document is inactive.
DCHECK(document().isActive());
if (!m_fontSelector)
return;
m_fontSelector->updateGenericFontFamilySettings(*m_document);
if (m_resolver)
m_resolver->invalidateMatchedPropertiesCache();
FontCache::fontCache()->invalidateShapeCache();
}
void StyleEngine::removeFontFaceRules(const HeapVector<Member<const StyleRuleFontFace>>& fontFaceRules)
{
if (!m_fontSelector)
return;
FontFaceCache* cache = m_fontSelector->fontFaceCache();
for (unsigned i = 0; i < fontFaceRules.size(); ++i)
cache->remove(fontFaceRules[i]);
if (m_resolver)
m_resolver->invalidateMatchedPropertiesCache();
}
void StyleEngine::markTreeScopeDirty(TreeScope& scope)
{
if (scope == m_document) {
markDocumentDirty();
return;
}
DCHECK(m_styleSheetCollectionMap.contains(&scope));
m_dirtyTreeScopes.add(&scope);
}
void StyleEngine::markDocumentDirty()
{
m_documentScopeDirty = true;
if (document().importLoader())
document().importsController()->master()->styleEngine().markDocumentDirty();
}
CSSStyleSheet* StyleEngine::createSheet(Element* e, const String& text, TextPosition startPosition, StyleEngineContext &context)
{
CSSStyleSheet* styleSheet = nullptr;
e->document().styleEngine().addPendingSheet(context);
AtomicString textContent(text);
HeapHashMap<AtomicString, Member<StyleSheetContents>>::AddResult result = m_textToSheetCache.add(textContent, nullptr);
if (result.isNewEntry || !result.storedValue->value) {
styleSheet = StyleEngine::parseSheet(e, text, startPosition);
if (result.isNewEntry && styleSheet->contents()->isCacheableForStyleElement()) {
result.storedValue->value = styleSheet->contents();
m_sheetToTextCache.add(styleSheet->contents(), textContent);
}
} else {
StyleSheetContents* contents = result.storedValue->value;
DCHECK(contents);
DCHECK(contents->isCacheableForStyleElement());
DCHECK_EQ(contents->singleOwnerDocument(), e->document());
styleSheet = CSSStyleSheet::createInline(contents, e, startPosition);
}
DCHECK(styleSheet);
styleSheet->setTitle(e->title());
if (!e->isInShadowTree())
setPreferredStylesheetSetNameIfNotSet(e->title());
return styleSheet;
}
CSSStyleSheet* StyleEngine::parseSheet(Element* e, const String& text, TextPosition startPosition)
{
CSSStyleSheet* styleSheet = nullptr;
styleSheet = CSSStyleSheet::createInline(e, KURL(), startPosition, e->document().characterSet());
styleSheet->contents()->parseStringAtPosition(text, startPosition);
return styleSheet;
}
void StyleEngine::removeSheet(StyleSheetContents* contents)
{
HeapHashMap<Member<StyleSheetContents>, AtomicString>::iterator it = m_sheetToTextCache.find(contents);
if (it == m_sheetToTextCache.end())
return;
m_textToSheetCache.remove(it->value);
m_sheetToTextCache.remove(contents);
}
void StyleEngine::collectScopedStyleFeaturesTo(RuleFeatureSet& features) const
{
HeapHashSet<Member<const StyleSheetContents>> visitedSharedStyleSheetContents;
if (document().scopedStyleResolver())
document().scopedStyleResolver()->collectFeaturesTo(features, visitedSharedStyleSheetContents);
for (TreeScope* treeScope : m_activeTreeScopes) {
// When creating StyleResolver, dirty treescopes might not be processed.
// So some active treescopes might not have a scoped style resolver.
// In this case, we should skip collectFeatures for the treescopes without
// scoped style resolvers. When invoking updateActiveStyleSheets,
// the treescope's features will be processed.
if (ScopedStyleResolver* resolver = treeScope->scopedStyleResolver())
resolver->collectFeaturesTo(features, visitedSharedStyleSheetContents);
}
}
void StyleEngine::fontsNeedUpdate(CSSFontSelector*)
{
if (!document().isActive())
return;
if (m_resolver)
m_resolver->invalidateMatchedPropertiesCache();
document().setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Fonts));
}
void StyleEngine::setFontSelector(CSSFontSelector* fontSelector)
{
if (m_fontSelector)
m_fontSelector->unregisterForInvalidationCallbacks(this);
m_fontSelector = fontSelector;
if (m_fontSelector)
m_fontSelector->registerForInvalidationCallbacks(this);
}
void StyleEngine::platformColorsChanged()
{
if (m_resolver)
m_resolver->invalidateMatchedPropertiesCache();
document().setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::PlatformColorChange));
}
bool StyleEngine::shouldSkipInvalidationFor(const Element& element) const
{
if (!resolver())
return true;
if (!element.inActiveDocument())
return true;
if (!element.parentNode())
return true;
return element.parentNode()->getStyleChangeType() >= SubtreeStyleChange;
}
void StyleEngine::classChangedForElement(const SpaceSplitString& changedClasses, Element& element)
{
if (shouldSkipInvalidationFor(element))
return;
InvalidationLists invalidationLists;
unsigned changedSize = changedClasses.size();
RuleFeatureSet& ruleFeatureSet = ensureResolver().ensureUpdatedRuleFeatureSet();
for (unsigned i = 0; i < changedSize; ++i)
ruleFeatureSet.collectInvalidationSetsForClass(invalidationLists, element, changedClasses[i]);
m_styleInvalidator.scheduleInvalidationSetsForElement(invalidationLists, element);
}
void StyleEngine::classChangedForElement(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses, Element& element)
{
if (shouldSkipInvalidationFor(element))
return;
if (!oldClasses.size()) {
classChangedForElement(newClasses, element);
return;
}
// Class vectors tend to be very short. This is faster than using a hash table.
BitVector remainingClassBits;
remainingClassBits.ensureSize(oldClasses.size());
InvalidationLists invalidationLists;
RuleFeatureSet& ruleFeatureSet = ensureResolver().ensureUpdatedRuleFeatureSet();
for (unsigned i = 0; i < newClasses.size(); ++i) {
bool found = false;
for (unsigned j = 0; j < oldClasses.size(); ++j) {
if (newClasses[i] == oldClasses[j]) {
// Mark each class that is still in the newClasses so we can skip doing
// an n^2 search below when looking for removals. We can't break from
// this loop early since a class can appear more than once.
remainingClassBits.quickSet(j);
found = true;
}
}
// Class was added.
if (!found)
ruleFeatureSet.collectInvalidationSetsForClass(invalidationLists, element, newClasses[i]);
}
for (unsigned i = 0; i < oldClasses.size(); ++i) {
if (remainingClassBits.quickGet(i))
continue;
// Class was removed.
ruleFeatureSet.collectInvalidationSetsForClass(invalidationLists, element, oldClasses[i]);
}
m_styleInvalidator.scheduleInvalidationSetsForElement(invalidationLists, element);
}
void StyleEngine::attributeChangedForElement(const QualifiedName& attributeName, Element& element)
{
if (shouldSkipInvalidationFor(element))
return;
InvalidationLists invalidationLists;
ensureResolver().ensureUpdatedRuleFeatureSet().collectInvalidationSetsForAttribute(invalidationLists, element, attributeName);
m_styleInvalidator.scheduleInvalidationSetsForElement(invalidationLists, element);
}
void StyleEngine::idChangedForElement(const AtomicString& oldId, const AtomicString& newId, Element& element)
{
if (shouldSkipInvalidationFor(element))
return;
InvalidationLists invalidationLists;
RuleFeatureSet& ruleFeatureSet = ensureResolver().ensureUpdatedRuleFeatureSet();
if (!oldId.isEmpty())
ruleFeatureSet.collectInvalidationSetsForId(invalidationLists, element, oldId);
if (!newId.isEmpty())
ruleFeatureSet.collectInvalidationSetsForId(invalidationLists, element, newId);
m_styleInvalidator.scheduleInvalidationSetsForElement(invalidationLists, element);
}
void StyleEngine::pseudoStateChangedForElement(CSSSelector::PseudoType pseudoType, Element& element)
{
if (shouldSkipInvalidationFor(element))
return;
InvalidationLists invalidationLists;
ensureResolver().ensureUpdatedRuleFeatureSet().collectInvalidationSetsForPseudoClass(invalidationLists, element, pseudoType);
m_styleInvalidator.scheduleInvalidationSetsForElement(invalidationLists, element);
}
void StyleEngine::setStatsEnabled(bool enabled)
{
if (!enabled) {
m_styleResolverStats = nullptr;
return;
}
if (!m_styleResolverStats)
m_styleResolverStats = StyleResolverStats::create();
else
m_styleResolverStats->reset();
}
void StyleEngine::setPreferredStylesheetSetNameIfNotSet(const String& name)
{
if (!m_preferredStylesheetSetName.isEmpty())
return;
m_preferredStylesheetSetName = name;
// TODO(rune@opera.com): Setting the selected set here is wrong if the set
// has been previously set by through Document.selectedStylesheetSet. Our
// current implementation ignores the effect of Document.selectedStylesheetSet
// and either only collects persistent style, or additionally preferred
// style when present. We are currently not marking the document scope dirty
// because preferred style is updated during active stylesheet update which
// would make this method re-entrant. Will need to change for async update.
m_selectedStylesheetSetName = name;
}
void StyleEngine::setSelectedStylesheetSetName(const String& name)
{
m_selectedStylesheetSetName = name;
// TODO(rune@opera.com): Setting Document.selectedStylesheetSet currently
// has no other effect than the ability to read back the set value using
// the same api. If it did have an effect, we should have marked the
// document scope dirty and triggered an update of the active stylesheets
// from here.
}
void StyleEngine::setHttpDefaultStyle(const String& content)
{
setPreferredStylesheetSetNameIfNotSet(content);
markDocumentDirty();
resolverChanged(FullStyleUpdate);
}
void StyleEngine::ensureFullscreenUAStyle()
{
CSSDefaultStyleSheets::instance().ensureDefaultStyleSheetForFullscreen();
if (!m_resolver)
return;
if (!m_resolver->hasFullscreenUAStyle())
m_resolver->resetRuleFeatures();
}
DEFINE_TRACE(StyleEngine)
{
visitor->trace(m_document);
visitor->trace(m_injectedAuthorStyleSheets);
visitor->trace(m_documentStyleSheetCollection);
visitor->trace(m_styleSheetCollectionMap);
visitor->trace(m_resolver);
visitor->trace(m_styleInvalidator);
visitor->trace(m_dirtyTreeScopes);
visitor->trace(m_activeTreeScopes);
visitor->trace(m_fontSelector);
visitor->trace(m_textToSheetCache);
visitor->trace(m_sheetToTextCache);
CSSFontSelectorClient::trace(visitor);
}
DEFINE_TRACE_WRAPPERS(StyleEngine)
{
for (auto sheet : m_injectedAuthorStyleSheets) {
visitor->traceWrappers(sheet);
}
visitor->traceWrappers(m_documentStyleSheetCollection);
}
} // namespace blink