2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 Apple Inc. All rights reserved.
7 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 * Copyright (C) 2008, 2009, 2011, 2012 Google Inc. All rights reserved.
9 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
10 * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved.
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
29 #include "core/dom/StyleEngine.h"
31 #include "HTMLNames.h"
33 #include "core/css/CSSFontSelector.h"
34 #include "core/css/CSSStyleSheet.h"
35 #include "core/css/FontFaceCache.h"
36 #include "core/css/StyleInvalidationAnalysis.h"
37 #include "core/css/StyleSheetContents.h"
38 #include "core/dom/DocumentStyleSheetCollector.h"
39 #include "core/dom/Element.h"
40 #include "core/dom/ProcessingInstruction.h"
41 #include "core/dom/ShadowTreeStyleSheetCollection.h"
42 #include "core/dom/shadow/ShadowRoot.h"
43 #include "core/html/HTMLIFrameElement.h"
44 #include "core/html/HTMLImport.h"
45 #include "core/html/HTMLLinkElement.h"
46 #include "core/inspector/InspectorInstrumentation.h"
47 #include "core/page/Page.h"
48 #include "core/page/PageGroup.h"
49 #include "core/frame/Settings.h"
50 #include "core/svg/SVGStyleElement.h"
51 #include "platform/URLPatternMatcher.h"
55 using namespace HTMLNames;
57 static HashMap<AtomicString, StyleSheetContents*>& textToSheetCache()
59 typedef HashMap<AtomicString, StyleSheetContents*> TextToSheetCache;
60 DEFINE_STATIC_LOCAL(TextToSheetCache, cache, ());
64 static HashMap<StyleSheetContents*, AtomicString>& sheetToTextCache()
66 typedef HashMap<StyleSheetContents*, AtomicString> SheetToTextCache;
67 DEFINE_STATIC_LOCAL(SheetToTextCache, cache, ());
71 StyleEngine::StyleEngine(Document& document)
72 : m_document(document)
73 , m_isMaster(HTMLImport::isMaster(&document))
74 , m_pendingStylesheets(0)
75 , m_injectedStyleSheetCacheValid(false)
76 , m_documentStyleSheetCollection(document)
77 , m_documentScopeDirty(true)
78 , m_usesSiblingRules(false)
79 , m_usesSiblingRulesOverride(false)
80 , m_usesFirstLineRules(false)
81 , m_usesFirstLetterRules(false)
82 , m_usesRemUnits(false)
83 , m_maxDirectAdjacentSelectors(0)
84 , m_ignorePendingStylesheets(false)
85 , m_didCalculateResolver(false)
86 // We don't need to create CSSFontSelector for imported document or
87 // HTMLTemplateElement's document, because those documents have no frame.
88 , m_fontSelector(document.frame() ? CSSFontSelector::create(&document) : 0)
92 StyleEngine::~StyleEngine()
94 for (unsigned i = 0; i < m_injectedAuthorStyleSheets.size(); ++i)
95 m_injectedAuthorStyleSheets[i]->clearOwnerNode();
96 for (unsigned i = 0; i < m_authorStyleSheets.size(); ++i)
97 m_authorStyleSheets[i]->clearOwnerNode();
100 m_fontSelector->clearDocument();
102 m_fontSelector->unregisterForInvalidationCallbacks(m_resolver.get());
106 inline Document* StyleEngine::master()
110 HTMLImport* import = m_document.import();
111 if (!import) // Document::import() can return null while executing its destructor.
113 return import->master();
116 void StyleEngine::insertTreeScopeInDocumentOrder(TreeScopeSet& treeScopes, TreeScope* treeScope)
118 if (treeScopes.isEmpty()) {
119 treeScopes.add(treeScope);
122 if (treeScopes.contains(treeScope))
125 TreeScopeSet::iterator begin = treeScopes.begin();
126 TreeScopeSet::iterator end = treeScopes.end();
127 TreeScopeSet::iterator it = end;
128 TreeScope* followingTreeScope = 0;
132 unsigned short position = n->comparePosition(*treeScope);
133 if (position & Node::DOCUMENT_POSITION_FOLLOWING) {
134 treeScopes.insertBefore(followingTreeScope, treeScope);
137 followingTreeScope = n;
138 } while (it != begin);
140 treeScopes.insertBefore(followingTreeScope, treeScope);
143 TreeScopeStyleSheetCollection* StyleEngine::ensureStyleSheetCollectionFor(TreeScope& treeScope)
145 if (treeScope == m_document)
146 return &m_documentStyleSheetCollection;
148 HashMap<TreeScope*, OwnPtr<TreeScopeStyleSheetCollection> >::AddResult result = m_styleSheetCollectionMap.add(&treeScope, nullptr);
149 if (result.isNewEntry)
150 result.storedValue->value = adoptPtr(new ShadowTreeStyleSheetCollection(toShadowRoot(treeScope)));
151 return result.storedValue->value.get();
154 TreeScopeStyleSheetCollection* StyleEngine::styleSheetCollectionFor(TreeScope& treeScope)
156 if (treeScope == m_document)
157 return &m_documentStyleSheetCollection;
159 HashMap<TreeScope*, OwnPtr<TreeScopeStyleSheetCollection> >::iterator it = m_styleSheetCollectionMap.find(&treeScope);
160 if (it == m_styleSheetCollectionMap.end())
162 return it->value.get();
165 const Vector<RefPtr<StyleSheet> >& StyleEngine::styleSheetsForStyleSheetList(TreeScope& treeScope)
167 if (treeScope == m_document)
168 return m_documentStyleSheetCollection.styleSheetsForStyleSheetList();
170 return ensureStyleSheetCollectionFor(treeScope)->styleSheetsForStyleSheetList();
173 const Vector<RefPtr<CSSStyleSheet> >& StyleEngine::activeAuthorStyleSheets() const
175 return m_documentStyleSheetCollection.activeAuthorStyleSheets();
178 void StyleEngine::combineCSSFeatureFlags(const RuleFeatureSet& features)
180 // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after).
181 m_usesSiblingRules = m_usesSiblingRules || features.usesSiblingRules();
182 m_usesFirstLineRules = m_usesFirstLineRules || features.usesFirstLineRules();
183 m_maxDirectAdjacentSelectors = max(m_maxDirectAdjacentSelectors, features.maxDirectAdjacentSelectors());
186 void StyleEngine::resetCSSFeatureFlags(const RuleFeatureSet& features)
188 m_usesSiblingRules = features.usesSiblingRules();
189 m_usesFirstLineRules = features.usesFirstLineRules();
190 m_maxDirectAdjacentSelectors = features.maxDirectAdjacentSelectors();
193 const Vector<RefPtr<CSSStyleSheet> >& StyleEngine::injectedAuthorStyleSheets() const
195 updateInjectedStyleSheetCache();
196 return m_injectedAuthorStyleSheets;
199 void StyleEngine::updateInjectedStyleSheetCache() const
201 if (m_injectedStyleSheetCacheValid)
203 m_injectedStyleSheetCacheValid = true;
204 m_injectedAuthorStyleSheets.clear();
206 Page* owningPage = m_document.page();
210 const InjectedStyleSheetEntryVector& entries = InjectedStyleSheets::instance().entries();
211 for (unsigned i = 0; i < entries.size(); ++i) {
212 const InjectedStyleSheetEntry* entry = entries[i].get();
213 if (entry->injectedFrames() == InjectStyleInTopFrameOnly && m_document.ownerElement())
215 if (!URLPatternMatcher::matchesPatterns(m_document.url(), entry->whitelist()))
217 RefPtr<CSSStyleSheet> groupSheet = CSSStyleSheet::createInline(const_cast<Document*>(&m_document), KURL());
218 m_injectedAuthorStyleSheets.append(groupSheet);
219 groupSheet->contents()->parseString(entry->source());
223 void StyleEngine::invalidateInjectedStyleSheetCache()
225 m_injectedStyleSheetCacheValid = false;
227 // FIXME: updateInjectedStyleSheetCache is called inside StyleSheetCollection::updateActiveStyleSheets
228 // and batch updates lots of sheets so we can't call addedStyleSheet() or removedStyleSheet().
229 m_document.styleResolverChanged(RecalcStyleDeferred);
232 void StyleEngine::addAuthorSheet(PassRefPtr<StyleSheetContents> authorSheet)
234 m_authorStyleSheets.append(CSSStyleSheet::create(authorSheet, &m_document));
235 m_document.addedStyleSheet(m_authorStyleSheets.last().get(), RecalcStyleImmediately);
239 void StyleEngine::addPendingSheet()
241 master()->styleEngine()->notifyPendingStyleSheetAdded();
244 // This method is called whenever a top-level stylesheet has finished loading.
245 void StyleEngine::removePendingSheet(Node* styleSheetCandidateNode, RemovePendingSheetNotificationType notification)
247 TreeScope* treeScope = styleSheetCandidateNode->hasTagName(styleTag) ? &styleSheetCandidateNode->treeScope() : &m_document;
248 markTreeScopeDirty(*treeScope);
249 master()->styleEngine()->notifyPendingStyleSheetRemoved(notification);
252 void StyleEngine::notifyPendingStyleSheetAdded()
255 m_pendingStylesheets++;
258 void StyleEngine::notifyPendingStyleSheetRemoved(RemovePendingSheetNotificationType notification)
261 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
262 ASSERT(m_pendingStylesheets > 0);
264 m_pendingStylesheets--;
265 if (m_pendingStylesheets)
268 if (notification == RemovePendingSheetNotifyLater) {
269 m_document.setNeedsNotifyRemoveAllPendingStylesheet();
273 // FIXME: We can't call addedStyleSheet or removedStyleSheet here because we don't know
274 // what's new. We should track that to tell the style system what changed.
275 m_document.didRemoveAllPendingStylesheet();
278 void StyleEngine::modifiedStyleSheet(StyleSheet* sheet)
283 Node* node = sheet->ownerNode();
284 if (!node || !node->inDocument())
287 TreeScope& treeScope = node->hasTagName(styleTag) ? node->treeScope() : m_document;
288 ASSERT(node->hasTagName(styleTag) || treeScope == m_document);
290 markTreeScopeDirty(treeScope);
293 void StyleEngine::addStyleSheetCandidateNode(Node* node, bool createdByParser)
295 if (!node->inDocument())
298 TreeScope& treeScope = node->hasTagName(styleTag) ? node->treeScope() : m_document;
299 ASSERT(node->hasTagName(styleTag) || treeScope == m_document);
301 TreeScopeStyleSheetCollection* collection = ensureStyleSheetCollectionFor(treeScope);
303 collection->addStyleSheetCandidateNode(node, createdByParser);
305 markTreeScopeDirty(treeScope);
306 if (treeScope != m_document)
307 insertTreeScopeInDocumentOrder(m_activeTreeScopes, &treeScope);
310 void StyleEngine::removeStyleSheetCandidateNode(Node* node, ContainerNode* scopingNode)
312 TreeScope& treeScope = scopingNode ? scopingNode->treeScope() : m_document;
313 ASSERT(node->hasTagName(styleTag) || treeScope == m_document);
315 TreeScopeStyleSheetCollection* collection = styleSheetCollectionFor(treeScope);
317 collection->removeStyleSheetCandidateNode(node, scopingNode);
319 markTreeScopeDirty(treeScope);
320 m_activeTreeScopes.remove(&treeScope);
323 void StyleEngine::modifiedStyleSheetCandidateNode(Node* node)
325 if (!node->inDocument())
328 TreeScope& treeScope = node->hasTagName(styleTag) ? node->treeScope() : m_document;
329 ASSERT(node->hasTagName(styleTag) || treeScope == m_document);
330 markTreeScopeDirty(treeScope);
333 bool StyleEngine::shouldUpdateShadowTreeStyleSheetCollection(StyleResolverUpdateMode updateMode)
335 return !m_dirtyTreeScopes.isEmpty() || updateMode == FullStyleUpdate;
338 void StyleEngine::clearMediaQueryRuleSetOnTreeScopeStyleSheets(TreeScopeSet treeScopes)
340 for (TreeScopeSet::iterator it = treeScopes.begin(); it != treeScopes.end(); ++it) {
341 TreeScope& treeScope = **it;
342 ASSERT(treeScope != m_document);
343 ShadowTreeStyleSheetCollection* collection = static_cast<ShadowTreeStyleSheetCollection*>(styleSheetCollectionFor(treeScope));
345 collection->clearMediaQueryRuleSetStyleSheets();
349 void StyleEngine::clearMediaQueryRuleSetStyleSheets()
351 m_documentStyleSheetCollection.clearMediaQueryRuleSetStyleSheets();
352 clearMediaQueryRuleSetOnTreeScopeStyleSheets(m_activeTreeScopes);
353 clearMediaQueryRuleSetOnTreeScopeStyleSheets(m_dirtyTreeScopes);
356 void StyleEngine::updateStyleSheetsInImport(DocumentStyleSheetCollector& parentCollector)
359 Vector<RefPtr<StyleSheet> > sheetsForList;
360 ImportedDocumentStyleSheetCollector subcollector(parentCollector, sheetsForList);
361 m_documentStyleSheetCollection.collectStyleSheets(this, subcollector);
362 m_documentStyleSheetCollection.swapSheetsForSheetList(sheetsForList);
365 bool StyleEngine::updateActiveStyleSheets(StyleResolverUpdateMode updateMode)
368 ASSERT(!m_document.inStyleRecalc());
370 if (!m_document.isActive())
373 bool requiresFullStyleRecalc = false;
374 if (m_documentScopeDirty || updateMode == FullStyleUpdate)
375 requiresFullStyleRecalc = m_documentStyleSheetCollection.updateActiveStyleSheets(this, updateMode);
377 if (shouldUpdateShadowTreeStyleSheetCollection(updateMode)) {
378 TreeScopeSet treeScopes = updateMode == FullStyleUpdate ? m_activeTreeScopes : m_dirtyTreeScopes;
379 HashSet<TreeScope*> treeScopesRemoved;
381 for (TreeScopeSet::iterator it = treeScopes.begin(); it != treeScopes.end(); ++it) {
382 TreeScope* treeScope = *it;
383 ASSERT(treeScope != m_document);
384 ShadowTreeStyleSheetCollection* collection = static_cast<ShadowTreeStyleSheetCollection*>(styleSheetCollectionFor(*treeScope));
386 collection->updateActiveStyleSheets(this, updateMode);
387 if (!collection->hasStyleSheetCandidateNodes())
388 treeScopesRemoved.add(treeScope);
390 if (!treeScopesRemoved.isEmpty())
391 for (HashSet<TreeScope*>::iterator it = treeScopesRemoved.begin(); it != treeScopesRemoved.end(); ++it)
392 m_activeTreeScopes.remove(*it);
395 InspectorInstrumentation::activeStyleSheetsUpdated(&m_document);
396 m_usesRemUnits = m_documentStyleSheetCollection.usesRemUnits();
398 m_dirtyTreeScopes.clear();
399 m_documentScopeDirty = false;
401 return requiresFullStyleRecalc;
404 const Vector<RefPtr<StyleSheet> > StyleEngine::activeStyleSheetsForInspector() const
406 if (m_activeTreeScopes.isEmpty())
407 return m_documentStyleSheetCollection.styleSheetsForStyleSheetList();
409 Vector<RefPtr<StyleSheet> > activeStyleSheets;
411 activeStyleSheets.append(m_documentStyleSheetCollection.styleSheetsForStyleSheetList());
413 TreeScopeSet::const_iterator begin = m_activeTreeScopes.begin();
414 TreeScopeSet::const_iterator end = m_activeTreeScopes.end();
415 for (TreeScopeSet::const_iterator it = begin; it != end; ++it) {
416 if (TreeScopeStyleSheetCollection* collection = m_styleSheetCollectionMap.get(*it))
417 activeStyleSheets.append(collection->styleSheetsForStyleSheetList());
420 // FIXME: Inspector needs a vector which has all active stylesheets.
421 // However, creating such a large vector might cause performance regression.
422 // Need to implement some smarter solution.
423 return activeStyleSheets;
426 void StyleEngine::didRemoveShadowRoot(ShadowRoot* shadowRoot)
428 m_styleSheetCollectionMap.remove(shadowRoot);
431 void StyleEngine::appendActiveAuthorStyleSheets()
435 m_resolver->setBuildScopedStyleTreeInDocumentOrder(true);
436 m_resolver->appendAuthorStyleSheets(0, m_documentStyleSheetCollection.activeAuthorStyleSheets());
438 TreeScopeSet::iterator begin = m_activeTreeScopes.begin();
439 TreeScopeSet::iterator end = m_activeTreeScopes.end();
440 for (TreeScopeSet::iterator it = begin; it != end; ++it) {
441 if (TreeScopeStyleSheetCollection* collection = m_styleSheetCollectionMap.get(*it)) {
442 m_resolver->setBuildScopedStyleTreeInDocumentOrder(!collection->scopingNodesForStyleScoped());
443 m_resolver->appendAuthorStyleSheets(0, collection->activeAuthorStyleSheets());
446 m_resolver->finishAppendAuthorStyleSheets();
447 m_resolver->setBuildScopedStyleTreeInDocumentOrder(false);
450 void StyleEngine::createResolver()
452 // It is a programming error to attempt to resolve style on a Document
453 // which is not in a frame. Code which hits this should have checked
454 // Document::isActive() before calling into code which could get here.
456 ASSERT(m_document.frame());
457 ASSERT(m_fontSelector);
459 m_resolver = adoptPtr(new StyleResolver(m_document));
460 appendActiveAuthorStyleSheets();
461 m_fontSelector->registerForInvalidationCallbacks(m_resolver.get());
462 combineCSSFeatureFlags(m_resolver->ensureRuleFeatureSet());
465 void StyleEngine::clearResolver()
467 ASSERT(!m_document.inStyleRecalc());
468 ASSERT(isMaster() || !m_resolver);
469 ASSERT(m_fontSelector || !m_resolver);
471 m_fontSelector->unregisterForInvalidationCallbacks(m_resolver.get());
475 void StyleEngine::clearMasterResolver()
477 if (Document* master = this->master())
478 master->styleEngine()->clearResolver();
481 unsigned StyleEngine::resolverAccessCount() const
483 return m_resolver ? m_resolver->accessCount() : 0;
486 void StyleEngine::didDetach()
491 bool StyleEngine::shouldClearResolver() const
493 return !m_didCalculateResolver && !haveStylesheetsLoaded();
496 StyleResolverChange StyleEngine::resolverChanged(RecalcStyleTime time, StyleResolverUpdateMode mode)
498 StyleResolverChange change;
501 if (Document* master = this->master())
502 master->styleResolverChanged(time, mode);
506 // Don't bother updating, since we haven't loaded all our style info yet
507 // and haven't calculated the style selector for the first time.
508 if (!m_document.isActive() || shouldClearResolver()) {
513 m_didCalculateResolver = true;
514 if (m_document.didLayoutWithPendingStylesheets() && !hasPendingSheets())
515 change.setNeedsRepaint();
517 if (updateActiveStyleSheets(mode))
518 change.setNeedsStyleRecalc();
523 void StyleEngine::clearFontCache()
525 // We should not recreate FontSelector. Instead, clear fontFaceCache.
527 m_fontSelector->fontFaceCache()->clear();
529 m_resolver->invalidateMatchedPropertiesCache();
532 void StyleEngine::updateGenericFontFamilySettings()
537 m_fontSelector->updateGenericFontFamilySettings(m_document);
539 m_resolver->invalidateMatchedPropertiesCache();
542 void StyleEngine::removeFontFaceRules(const Vector<const StyleRuleFontFace*>& fontFaceRules)
547 FontFaceCache* cache = m_fontSelector->fontFaceCache();
548 for (unsigned i = 0; i < fontFaceRules.size(); ++i)
549 cache->remove(fontFaceRules[i]);
551 m_resolver->invalidateMatchedPropertiesCache();
554 void StyleEngine::markTreeScopeDirty(TreeScope& scope)
556 if (scope == m_document) {
561 m_dirtyTreeScopes.add(&scope);
564 void StyleEngine::markDocumentDirty()
566 m_documentScopeDirty = true;
567 if (!HTMLImport::isMaster(&m_document))
568 m_document.import()->master()->styleEngine()->markDocumentDirty();
571 PassRefPtr<CSSStyleSheet> StyleEngine::createSheet(Element* e, const String& text, TextPosition startPosition, bool createdByParser)
573 RefPtr<CSSStyleSheet> styleSheet;
575 e->document().styleEngine()->addPendingSheet();
577 if (!e->document().inQuirksMode()) {
578 AtomicString textContent(text);
580 HashMap<AtomicString, StyleSheetContents*>::AddResult result = textToSheetCache().add(textContent, 0);
581 if (result.isNewEntry || !result.storedValue->value) {
582 styleSheet = StyleEngine::parseSheet(e, text, startPosition, createdByParser);
583 if (result.isNewEntry && styleSheet->contents()->maybeCacheable()) {
584 result.storedValue->value = styleSheet->contents();
585 sheetToTextCache().add(styleSheet->contents(), textContent);
588 ASSERT(result.storedValue->value->maybeCacheable());
589 styleSheet = CSSStyleSheet::createInline(result.storedValue->value, e, startPosition);
592 // FIXME: currently we don't cache StyleSheetContents inQuirksMode.
593 styleSheet = StyleEngine::parseSheet(e, text, startPosition, createdByParser);
597 styleSheet->setTitle(e->title());
601 PassRefPtr<CSSStyleSheet> StyleEngine::parseSheet(Element* e, const String& text, TextPosition startPosition, bool createdByParser)
603 RefPtr<CSSStyleSheet> styleSheet;
604 styleSheet = CSSStyleSheet::createInline(e, KURL(), startPosition, e->document().inputEncoding());
605 styleSheet->contents()->parseStringAtPosition(text, startPosition, createdByParser);
609 void StyleEngine::removeSheet(StyleSheetContents* contents)
611 HashMap<StyleSheetContents*, AtomicString>::iterator it = sheetToTextCache().find(contents);
612 if (it == sheetToTextCache().end())
615 textToSheetCache().remove(it->value);
616 sheetToTextCache().remove(contents);