1/*
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.
11 *
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.
16 *
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.
21 *
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.
26 */
27
28#include "config.h"
29#include "core/dom/StyleEngine.h"
30
31#include "core/HTMLNames.h"
32#include "core/css/CSSFontSelector.h"
33#include "core/css/CSSStyleSheet.h"
34#include "core/css/FontFaceCache.h"
35#include "core/css/StyleSheetContents.h"
36#include "core/dom/DocumentStyleSheetCollector.h"
37#include "core/dom/Element.h"
38#include "core/dom/ProcessingInstruction.h"
39#include "core/dom/ShadowTreeStyleSheetCollection.h"
40#include "core/dom/shadow/ShadowRoot.h"
41#include "core/html/HTMLIFrameElement.h"
42#include "core/html/HTMLLinkElement.h"
43#include "core/html/imports/HTMLImportsController.h"
44#include "core/inspector/InspectorInstrumentation.h"
45#include "core/page/InjectedStyleSheets.h"
46#include "core/page/Page.h"
47#include "core/frame/Settings.h"
48#include "platform/URLPatternMatcher.h"
49
50namespace blink {
51
52using namespace HTMLNames;
53
54StyleEngine::StyleEngine(Document& document)
55    : m_document(&document)
56    , m_isMaster(!document.importsController() || document.importsController()->master() == &document)
57    , m_pendingStylesheets(0)
58    , m_injectedStyleSheetCacheValid(false)
59    , m_documentStyleSheetCollection(DocumentStyleSheetCollection::create(document))
60    , m_documentScopeDirty(true)
61    , m_usesSiblingRules(false)
62    , m_usesSiblingRulesOverride(false)
63    , m_usesFirstLineRules(false)
64    , m_usesFirstLetterRules(false)
65    , m_usesRemUnits(false)
66    , m_maxDirectAdjacentSelectors(0)
67    , m_ignorePendingStylesheets(false)
68    , m_didCalculateResolver(false)
69    // We don't need to create CSSFontSelector for imported document or
70    // HTMLTemplateElement's document, because those documents have no frame.
71    , m_fontSelector(document.frame() ? CSSFontSelector::create(&document) : nullptr)
72    , m_xslStyleSheet(nullptr)
73{
74    if (m_fontSelector)
75        m_fontSelector->registerForInvalidationCallbacks(this);
76}
77
78StyleEngine::~StyleEngine()
79{
80}
81
82#if !ENABLE(OILPAN)
83void StyleEngine::detachFromDocument()
84{
85    // Cleanup is performed eagerly when the StyleEngine is removed from the
86    // document. The StyleEngine is unreachable after this, since only the
87    // document has a reference to it.
88    for (unsigned i = 0; i < m_injectedAuthorStyleSheets.size(); ++i)
89        m_injectedAuthorStyleSheets[i]->clearOwnerNode();
90    for (unsigned i = 0; i < m_authorStyleSheets.size(); ++i)
91        m_authorStyleSheets[i]->clearOwnerNode();
92
93    if (m_fontSelector) {
94        m_fontSelector->clearDocument();
95        m_fontSelector->unregisterForInvalidationCallbacks(this);
96    }
97
98    // Decrement reference counts for things we could be keeping alive.
99    m_fontSelector.clear();
100    m_resolver.clear();
101    m_styleSheetCollectionMap.clear();
102    for (ScopedStyleResolverSet::iterator it = m_scopedStyleResolvers.begin(); it != m_scopedStyleResolvers.end(); ++it)
103        const_cast<TreeScope&>((*it)->treeScope()).clearScopedStyleResolver();
104    m_scopedStyleResolvers.clear();
105}
106#endif
107
108inline Document* StyleEngine::master()
109{
110    if (isMaster())
111        return m_document;
112    HTMLImportsController* import = document().importsController();
113    if (!import) // Document::import() can return null while executing its destructor.
114        return 0;
115    return import->master();
116}
117
118void StyleEngine::insertTreeScopeInDocumentOrder(TreeScopeSet& treeScopes, TreeScope* treeScope)
119{
120    if (treeScopes.isEmpty()) {
121        treeScopes.add(treeScope);
122        return;
123    }
124    if (treeScopes.contains(treeScope))
125        return;
126
127    TreeScopeSet::iterator begin = treeScopes.begin();
128    TreeScopeSet::iterator end = treeScopes.end();
129    TreeScopeSet::iterator it = end;
130    TreeScope* followingTreeScope = 0;
131    do {
132        --it;
133        TreeScope* n = *it;
134        unsigned short position = n->comparePosition(*treeScope);
135        if (position & Node::DOCUMENT_POSITION_FOLLOWING) {
136            treeScopes.insertBefore(followingTreeScope, treeScope);
137            return;
138        }
139        followingTreeScope = n;
140    } while (it != begin);
141
142    treeScopes.insertBefore(followingTreeScope, treeScope);
143}
144
145TreeScopeStyleSheetCollection* StyleEngine::ensureStyleSheetCollectionFor(TreeScope& treeScope)
146{
147    if (treeScope == m_document)
148        return documentStyleSheetCollection();
149
150    StyleSheetCollectionMap::AddResult result = m_styleSheetCollectionMap.add(&treeScope, nullptr);
151    if (result.isNewEntry)
152        result.storedValue->value = adoptPtrWillBeNoop(new ShadowTreeStyleSheetCollection(toShadowRoot(treeScope)));
153    return result.storedValue->value.get();
154}
155
156TreeScopeStyleSheetCollection* StyleEngine::styleSheetCollectionFor(TreeScope& treeScope)
157{
158    if (treeScope == m_document)
159        return documentStyleSheetCollection();
160
161    StyleSheetCollectionMap::iterator it = m_styleSheetCollectionMap.find(&treeScope);
162    if (it == m_styleSheetCollectionMap.end())
163        return 0;
164    return it->value.get();
165}
166
167const WillBeHeapVector<RefPtrWillBeMember<StyleSheet> >& StyleEngine::styleSheetsForStyleSheetList(TreeScope& treeScope)
168{
169    if (treeScope == m_document)
170        return documentStyleSheetCollection()->styleSheetsForStyleSheetList();
171
172    return ensureStyleSheetCollectionFor(treeScope)->styleSheetsForStyleSheetList();
173}
174
175const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& StyleEngine::activeAuthorStyleSheets() const
176{
177    return documentStyleSheetCollection()->activeAuthorStyleSheets();
178}
179
180void StyleEngine::combineCSSFeatureFlags(const RuleFeatureSet& features)
181{
182    // 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).
183    m_usesSiblingRules = m_usesSiblingRules || features.usesSiblingRules();
184    m_usesFirstLineRules = m_usesFirstLineRules || features.usesFirstLineRules();
185    m_maxDirectAdjacentSelectors = max(m_maxDirectAdjacentSelectors, features.maxDirectAdjacentSelectors());
186}
187
188void StyleEngine::resetCSSFeatureFlags(const RuleFeatureSet& features)
189{
190    m_usesSiblingRules = features.usesSiblingRules();
191    m_usesFirstLineRules = features.usesFirstLineRules();
192    m_maxDirectAdjacentSelectors = features.maxDirectAdjacentSelectors();
193}
194
195const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& StyleEngine::injectedAuthorStyleSheets() const
196{
197    updateInjectedStyleSheetCache();
198    return m_injectedAuthorStyleSheets;
199}
200
201void StyleEngine::updateInjectedStyleSheetCache() const
202{
203    if (m_injectedStyleSheetCacheValid)
204        return;
205    m_injectedStyleSheetCacheValid = true;
206    m_injectedAuthorStyleSheets.clear();
207
208    Page* owningPage = document().page();
209    if (!owningPage)
210        return;
211
212    const InjectedStyleSheetEntryVector& entries = InjectedStyleSheets::instance().entries();
213    for (unsigned i = 0; i < entries.size(); ++i) {
214        const InjectedStyleSheetEntry* entry = entries[i].get();
215        if (entry->injectedFrames() == InjectStyleInTopFrameOnly && document().ownerElement())
216            continue;
217        if (!URLPatternMatcher::matchesPatterns(document().url(), entry->whitelist()))
218            continue;
219        RefPtrWillBeRawPtr<CSSStyleSheet> groupSheet = CSSStyleSheet::createInline(m_document, KURL());
220        m_injectedAuthorStyleSheets.append(groupSheet);
221        groupSheet->contents()->parseString(entry->source());
222    }
223}
224
225void StyleEngine::invalidateInjectedStyleSheetCache()
226{
227    m_injectedStyleSheetCacheValid = false;
228    markDocumentDirty();
229    // FIXME: updateInjectedStyleSheetCache is called inside StyleSheetCollection::updateActiveStyleSheets
230    // and batch updates lots of sheets so we can't call addedStyleSheet() or removedStyleSheet().
231    document().styleResolverChanged();
232}
233
234void StyleEngine::compatibilityModeChanged()
235{
236    if (!m_injectedAuthorStyleSheets.isEmpty())
237        invalidateInjectedStyleSheetCache();
238}
239
240void StyleEngine::addAuthorSheet(PassRefPtrWillBeRawPtr<StyleSheetContents> authorSheet)
241{
242    m_authorStyleSheets.append(CSSStyleSheet::create(authorSheet, m_document));
243    document().addedStyleSheet(m_authorStyleSheets.last().get());
244    markDocumentDirty();
245}
246
247void StyleEngine::addPendingSheet()
248{
249    m_pendingStylesheets++;
250}
251
252// This method is called whenever a top-level stylesheet has finished loading.
253void StyleEngine::removePendingSheet(Node* styleSheetCandidateNode)
254{
255    ASSERT(styleSheetCandidateNode);
256    TreeScope* treeScope = isHTMLStyleElement(*styleSheetCandidateNode) ? &styleSheetCandidateNode->treeScope() : m_document.get();
257    markTreeScopeDirty(*treeScope);
258
259    // Make sure we knew this sheet was pending, and that our count isn't out of sync.
260    ASSERT(m_pendingStylesheets > 0);
261
262    m_pendingStylesheets--;
263    if (m_pendingStylesheets)
264        return;
265
266    // FIXME: We can't call addedStyleSheet or removedStyleSheet here because we don't know
267    // what's new. We should track that to tell the style system what changed.
268    document().didRemoveAllPendingStylesheet();
269}
270
271void StyleEngine::modifiedStyleSheet(StyleSheet* sheet)
272{
273    if (!sheet)
274        return;
275
276    Node* node = sheet->ownerNode();
277    if (!node || !node->inDocument())
278        return;
279
280    TreeScope& treeScope = isHTMLStyleElement(*node) ? node->treeScope() : *m_document;
281    ASSERT(isHTMLStyleElement(node) || treeScope == m_document);
282
283    markTreeScopeDirty(treeScope);
284}
285
286void StyleEngine::addStyleSheetCandidateNode(Node* node, bool createdByParser)
287{
288    if (!node->inDocument())
289        return;
290
291    TreeScope& treeScope = isHTMLStyleElement(*node) ? node->treeScope() : *m_document;
292    ASSERT(isHTMLStyleElement(node) || treeScope == m_document);
293    ASSERT(!isXSLStyleSheet(*node));
294    TreeScopeStyleSheetCollection* collection = ensureStyleSheetCollectionFor(treeScope);
295    ASSERT(collection);
296    collection->addStyleSheetCandidateNode(node, createdByParser);
297
298    markTreeScopeDirty(treeScope);
299    if (treeScope != m_document)
300        insertTreeScopeInDocumentOrder(m_activeTreeScopes, &treeScope);
301}
302
303void StyleEngine::removeStyleSheetCandidateNode(Node* node)
304{
305    removeStyleSheetCandidateNode(node, 0, *m_document);
306}
307
308void StyleEngine::removeStyleSheetCandidateNode(Node* node, ContainerNode* scopingNode, TreeScope& treeScope)
309{
310    ASSERT(isHTMLStyleElement(node) || treeScope == m_document);
311    ASSERT(!isXSLStyleSheet(*node));
312
313    TreeScopeStyleSheetCollection* collection = styleSheetCollectionFor(treeScope);
314    ASSERT(collection);
315    collection->removeStyleSheetCandidateNode(node, scopingNode);
316
317    markTreeScopeDirty(treeScope);
318    m_activeTreeScopes.remove(&treeScope);
319}
320
321void StyleEngine::addXSLStyleSheet(ProcessingInstruction* node, bool createdByParser)
322{
323    if (!node->inDocument())
324        return;
325
326    ASSERT(isXSLStyleSheet(*node));
327    bool needToUpdate = false;
328    if (createdByParser || !m_xslStyleSheet) {
329        needToUpdate = !m_xslStyleSheet;
330    } else {
331        unsigned position = m_xslStyleSheet->compareDocumentPosition(node, Node::TreatShadowTreesAsDisconnected);
332        needToUpdate = position & Node::DOCUMENT_POSITION_FOLLOWING;
333    }
334
335    if (!needToUpdate)
336        return;
337
338    markTreeScopeDirty(*m_document);
339    m_xslStyleSheet = node;
340}
341
342void StyleEngine::removeXSLStyleSheet(ProcessingInstruction* node)
343{
344    ASSERT(isXSLStyleSheet(*node));
345    if (m_xslStyleSheet != node)
346        return;
347
348    markTreeScopeDirty(*m_document);
349    m_xslStyleSheet = nullptr;
350}
351
352void StyleEngine::modifiedStyleSheetCandidateNode(Node* node)
353{
354    if (!node->inDocument())
355        return;
356
357    TreeScope& treeScope = isHTMLStyleElement(*node) ? node->treeScope() : *m_document;
358    ASSERT(isHTMLStyleElement(node) || treeScope == m_document);
359    markTreeScopeDirty(treeScope);
360}
361
362void StyleEngine::enableExitTransitionStylesheets()
363{
364    TreeScopeStyleSheetCollection* collection = ensureStyleSheetCollectionFor(*m_document);
365    collection->enableExitTransitionStylesheets();
366}
367
368bool StyleEngine::shouldUpdateDocumentStyleSheetCollection(StyleResolverUpdateMode updateMode) const
369{
370    return m_documentScopeDirty || updateMode == FullStyleUpdate;
371}
372
373bool StyleEngine::shouldUpdateShadowTreeStyleSheetCollection(StyleResolverUpdateMode updateMode) const
374{
375    return !m_dirtyTreeScopes.isEmpty() || updateMode == FullStyleUpdate;
376}
377
378void StyleEngine::clearMediaQueryRuleSetOnTreeScopeStyleSheets(TreeScopeSet treeScopes)
379{
380    for (TreeScopeSet::iterator it = treeScopes.begin(); it != treeScopes.end(); ++it) {
381        TreeScope& treeScope = **it;
382        ASSERT(treeScope != m_document);
383        ShadowTreeStyleSheetCollection* collection = static_cast<ShadowTreeStyleSheetCollection*>(styleSheetCollectionFor(treeScope));
384        ASSERT(collection);
385        collection->clearMediaQueryRuleSetStyleSheets();
386    }
387}
388
389void StyleEngine::clearMediaQueryRuleSetStyleSheets()
390{
391    documentStyleSheetCollection()->clearMediaQueryRuleSetStyleSheets();
392    clearMediaQueryRuleSetOnTreeScopeStyleSheets(m_activeTreeScopes);
393    clearMediaQueryRuleSetOnTreeScopeStyleSheets(m_dirtyTreeScopes);
394}
395
396void StyleEngine::updateStyleSheetsInImport(DocumentStyleSheetCollector& parentCollector)
397{
398    ASSERT(!isMaster());
399    WillBeHeapVector<RefPtrWillBeMember<StyleSheet> > sheetsForList;
400    ImportedDocumentStyleSheetCollector subcollector(parentCollector, sheetsForList);
401    documentStyleSheetCollection()->collectStyleSheets(this, subcollector);
402    documentStyleSheetCollection()->swapSheetsForSheetList(sheetsForList);
403}
404
405void StyleEngine::updateActiveStyleSheets(StyleResolverUpdateMode updateMode)
406{
407    ASSERT(isMaster());
408    ASSERT(!document().inStyleRecalc());
409
410    if (!document().isActive())
411        return;
412
413    if (shouldUpdateDocumentStyleSheetCollection(updateMode))
414        documentStyleSheetCollection()->updateActiveStyleSheets(this, updateMode);
415
416    if (shouldUpdateShadowTreeStyleSheetCollection(updateMode)) {
417        TreeScopeSet treeScopes = updateMode == FullStyleUpdate ? m_activeTreeScopes : m_dirtyTreeScopes;
418        HashSet<TreeScope*> treeScopesRemoved;
419
420        for (TreeScopeSet::iterator it = treeScopes.begin(); it != treeScopes.end(); ++it) {
421            TreeScope* treeScope = *it;
422            ASSERT(treeScope != m_document);
423            ShadowTreeStyleSheetCollection* collection = static_cast<ShadowTreeStyleSheetCollection*>(styleSheetCollectionFor(*treeScope));
424            ASSERT(collection);
425            collection->updateActiveStyleSheets(this, updateMode);
426            if (!collection->hasStyleSheetCandidateNodes())
427                treeScopesRemoved.add(treeScope);
428        }
429        m_activeTreeScopes.removeAll(treeScopesRemoved);
430    }
431
432    InspectorInstrumentation::activeStyleSheetsUpdated(m_document);
433    m_usesRemUnits = documentStyleSheetCollection()->usesRemUnits();
434
435    m_dirtyTreeScopes.clear();
436    m_documentScopeDirty = false;
437}
438
439const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > StyleEngine::activeStyleSheetsForInspector() const
440{
441    if (m_activeTreeScopes.isEmpty())
442        return documentStyleSheetCollection()->activeAuthorStyleSheets();
443
444    WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > activeStyleSheets;
445
446    activeStyleSheets.appendVector(documentStyleSheetCollection()->activeAuthorStyleSheets());
447
448    TreeScopeSet::const_iterator begin = m_activeTreeScopes.begin();
449    TreeScopeSet::const_iterator end = m_activeTreeScopes.end();
450    for (TreeScopeSet::const_iterator it = begin; it != end; ++it) {
451        if (TreeScopeStyleSheetCollection* collection = m_styleSheetCollectionMap.get(*it))
452            activeStyleSheets.appendVector(collection->activeAuthorStyleSheets());
453    }
454
455    // FIXME: Inspector needs a vector which has all active stylesheets.
456    // However, creating such a large vector might cause performance regression.
457    // Need to implement some smarter solution.
458    return activeStyleSheets;
459}
460
461void StyleEngine::didRemoveShadowRoot(ShadowRoot* shadowRoot)
462{
463    if (shadowRoot->scopedStyleResolver())
464        removeScopedStyleResolver(shadowRoot->scopedStyleResolver());
465    m_styleSheetCollectionMap.remove(shadowRoot);
466}
467
468void StyleEngine::appendActiveAuthorStyleSheets()
469{
470    ASSERT(isMaster());
471
472    m_resolver->appendAuthorStyleSheets(documentStyleSheetCollection()->activeAuthorStyleSheets());
473
474    TreeScopeSet::iterator begin = m_activeTreeScopes.begin();
475    TreeScopeSet::iterator end = m_activeTreeScopes.end();
476    for (TreeScopeSet::iterator it = begin; it != end; ++it) {
477        if (TreeScopeStyleSheetCollection* collection = m_styleSheetCollectionMap.get(*it))
478            m_resolver->appendAuthorStyleSheets(collection->activeAuthorStyleSheets());
479    }
480    m_resolver->finishAppendAuthorStyleSheets();
481}
482
483void StyleEngine::createResolver()
484{
485    // It is a programming error to attempt to resolve style on a Document
486    // which is not in a frame. Code which hits this should have checked
487    // Document::isActive() before calling into code which could get here.
488
489    ASSERT(document().frame());
490
491    m_resolver = adoptPtrWillBeNoop(new StyleResolver(*m_document));
492    addScopedStyleResolver(&m_document->ensureScopedStyleResolver());
493
494    appendActiveAuthorStyleSheets();
495    combineCSSFeatureFlags(m_resolver->ensureUpdatedRuleFeatureSet());
496}
497
498void StyleEngine::clearResolver()
499{
500    ASSERT(!document().inStyleRecalc());
501    ASSERT(isMaster() || !m_resolver);
502
503    for (ScopedStyleResolverSet::iterator it = m_scopedStyleResolvers.begin(); it != m_scopedStyleResolvers.end(); ++it)
504        const_cast<TreeScope&>((*it)->treeScope()).clearScopedStyleResolver();
505    m_scopedStyleResolvers.clear();
506
507    if (m_resolver)
508        document().updateStyleInvalidationIfNeeded();
509    m_resolver.clear();
510}
511
512void StyleEngine::clearMasterResolver()
513{
514    if (Document* master = this->master())
515        master->styleEngine()->clearResolver();
516}
517
518unsigned StyleEngine::resolverAccessCount() const
519{
520    return m_resolver ? m_resolver->accessCount() : 0;
521}
522
523void StyleEngine::didDetach()
524{
525    clearResolver();
526}
527
528bool StyleEngine::shouldClearResolver() const
529{
530    return !m_didCalculateResolver && !haveStylesheetsLoaded();
531}
532
533bool StyleEngine::shouldApplyXSLTransform() const
534{
535    if (!RuntimeEnabledFeatures::xsltEnabled())
536        return false;
537    return m_xslStyleSheet && !m_document->transformSourceDocument();
538}
539
540void StyleEngine::resolverChanged(StyleResolverUpdateMode mode)
541{
542    if (!isMaster()) {
543        if (Document* master = this->master())
544            master->styleResolverChanged(mode);
545        return;
546    }
547
548    // Don't bother updating, since we haven't loaded all our style info yet
549    // and haven't calculated the style selector for the first time.
550    if (!document().isActive() || shouldClearResolver()) {
551        clearResolver();
552        return;
553    }
554
555    if (shouldApplyXSLTransform()) {
556        // Processing instruction (XML documents only).
557        // We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
558        // Don't apply XSL transforms to already transformed documents -- <rdar://problem/4132806>
559        if (!m_document->parsing() && !m_xslStyleSheet->isLoading())
560            m_document->applyXSLTransform(m_xslStyleSheet.get());
561        return;
562    }
563
564    m_didCalculateResolver = true;
565    updateActiveStyleSheets(mode);
566}
567
568void StyleEngine::clearFontCache()
569{
570    if (m_fontSelector)
571        m_fontSelector->fontFaceCache()->clearCSSConnected();
572    if (m_resolver)
573        m_resolver->invalidateMatchedPropertiesCache();
574}
575
576void StyleEngine::updateGenericFontFamilySettings()
577{
578    // FIXME: we should not update generic font family settings when
579    // document is inactive.
580    ASSERT(document().isActive());
581
582    if (!m_fontSelector)
583        return;
584
585    m_fontSelector->updateGenericFontFamilySettings(*m_document);
586    if (m_resolver)
587        m_resolver->invalidateMatchedPropertiesCache();
588}
589
590void StyleEngine::removeFontFaceRules(const WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >& fontFaceRules)
591{
592    if (!m_fontSelector)
593        return;
594
595    FontFaceCache* cache = m_fontSelector->fontFaceCache();
596    for (unsigned i = 0; i < fontFaceRules.size(); ++i)
597        cache->remove(fontFaceRules[i]);
598    if (m_resolver)
599        m_resolver->invalidateMatchedPropertiesCache();
600}
601
602void StyleEngine::markTreeScopeDirty(TreeScope& scope)
603{
604    if (scope == m_document) {
605        markDocumentDirty();
606        return;
607    }
608
609    m_dirtyTreeScopes.add(&scope);
610}
611
612void StyleEngine::markDocumentDirty()
613{
614    m_documentScopeDirty = true;
615    if (document().importLoader())
616        document().importsController()->master()->styleEngine()->markDocumentDirty();
617}
618
619static bool isCacheableForStyleElement(const StyleSheetContents& contents)
620{
621    // FIXME: Support copying import rules.
622    if (!contents.importRules().isEmpty())
623        return false;
624    // Until import rules are supported in cached sheets it's not possible for loading to fail.
625    ASSERT(!contents.didLoadErrorOccur());
626    // It is not the original sheet anymore.
627    if (contents.isMutable())
628        return false;
629    if (!contents.hasSyntacticallyValidCSSHeader())
630        return false;
631    return true;
632}
633
634PassRefPtrWillBeRawPtr<CSSStyleSheet> StyleEngine::createSheet(Element* e, const String& text, TextPosition startPosition, bool createdByParser)
635{
636    RefPtrWillBeRawPtr<CSSStyleSheet> styleSheet = nullptr;
637
638    e->document().styleEngine()->addPendingSheet();
639
640    if (!e->document().inQuirksMode()) {
641        AtomicString textContent(text);
642
643        WillBeHeapHashMap<AtomicString, RawPtrWillBeMember<StyleSheetContents> >::AddResult result = m_textToSheetCache.add(textContent, nullptr);
644        if (result.isNewEntry || !result.storedValue->value) {
645            styleSheet = StyleEngine::parseSheet(e, text, startPosition, createdByParser);
646            if (result.isNewEntry && isCacheableForStyleElement(*styleSheet->contents())) {
647                result.storedValue->value = styleSheet->contents();
648                m_sheetToTextCache.add(styleSheet->contents(), textContent);
649            }
650        } else {
651            StyleSheetContents* contents = result.storedValue->value;
652            ASSERT(contents);
653            ASSERT(isCacheableForStyleElement(*contents));
654            ASSERT(contents->singleOwnerDocument() == e->document());
655            styleSheet = CSSStyleSheet::createInline(contents, e, startPosition);
656        }
657    } else {
658        // FIXME: currently we don't cache StyleSheetContents inQuirksMode.
659        styleSheet = StyleEngine::parseSheet(e, text, startPosition, createdByParser);
660    }
661
662    ASSERT(styleSheet);
663    styleSheet->setTitle(e->title());
664    return styleSheet;
665}
666
667PassRefPtrWillBeRawPtr<CSSStyleSheet> StyleEngine::parseSheet(Element* e, const String& text, TextPosition startPosition, bool createdByParser)
668{
669    RefPtrWillBeRawPtr<CSSStyleSheet> styleSheet = nullptr;
670    styleSheet = CSSStyleSheet::createInline(e, KURL(), startPosition, e->document().inputEncoding());
671    styleSheet->contents()->parseStringAtPosition(text, startPosition, createdByParser);
672    return styleSheet;
673}
674
675void StyleEngine::removeSheet(StyleSheetContents* contents)
676{
677    WillBeHeapHashMap<RawPtrWillBeMember<StyleSheetContents>, AtomicString>::iterator it = m_sheetToTextCache.find(contents);
678    if (it == m_sheetToTextCache.end())
679        return;
680
681    m_textToSheetCache.remove(it->value);
682    m_sheetToTextCache.remove(contents);
683}
684
685void StyleEngine::collectScopedStyleFeaturesTo(RuleFeatureSet& features) const
686{
687    HashSet<const StyleSheetContents*> visitedSharedStyleSheetContents;
688    for (ScopedStyleResolverSet::iterator it = m_scopedStyleResolvers.begin(); it != m_scopedStyleResolvers.end(); ++it)
689        (*it)->collectFeaturesTo(features, visitedSharedStyleSheetContents);
690}
691
692void StyleEngine::fontsNeedUpdate(CSSFontSelector*)
693{
694    if (!document().isActive())
695        return;
696
697    if (m_resolver)
698        m_resolver->invalidateMatchedPropertiesCache();
699    document().setNeedsStyleRecalc(SubtreeStyleChange);
700}
701
702void StyleEngine::trace(Visitor* visitor)
703{
704#if ENABLE(OILPAN)
705    visitor->trace(m_document);
706    visitor->trace(m_injectedAuthorStyleSheets);
707    visitor->trace(m_authorStyleSheets);
708    visitor->trace(m_documentStyleSheetCollection);
709    visitor->trace(m_styleSheetCollectionMap);
710    visitor->trace(m_scopedStyleResolvers);
711    visitor->trace(m_resolver);
712    visitor->trace(m_fontSelector);
713    visitor->trace(m_textToSheetCache);
714    visitor->trace(m_sheetToTextCache);
715    visitor->trace(m_xslStyleSheet);
716#endif
717    CSSFontSelectorClient::trace(visitor);
718}
719
720}
721