Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2001 Dirk Mueller (mueller (at) kde.org)
      5  *           (C) 2006 Alexey Proskuryakov (ap (at) webkit.org)
      6  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
      7  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      8  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
      9  * Copyright (C) 2013 Google Inc. All rights reserved.
     10  *
     11  * This library is free software; you can redistribute it and/or
     12  * modify it under the terms of the GNU Library General Public
     13  * License as published by the Free Software Foundation; either
     14  * version 2 of the License, or (at your option) any later version.
     15  *
     16  * This library is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     19  * Library General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU Library General Public License
     22  * along with this library; see the file COPYING.LIB.  If not, write to
     23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     24  * Boston, MA 02110-1301, USA.
     25  */
     26 
     27 #include "config.h"
     28 #include "core/dom/TreeScopeStyleSheetCollection.h"
     29 
     30 #include "core/css/CSSStyleSheet.h"
     31 #include "core/css/StyleRuleImport.h"
     32 #include "core/css/StyleSheetContents.h"
     33 #include "core/css/invalidation/StyleSheetInvalidationAnalysis.h"
     34 #include "core/css/resolver/StyleResolver.h"
     35 #include "core/dom/Element.h"
     36 #include "core/dom/StyleEngine.h"
     37 #include "core/html/HTMLLinkElement.h"
     38 #include "core/html/HTMLStyleElement.h"
     39 
     40 namespace blink {
     41 
     42 TreeScopeStyleSheetCollection::TreeScopeStyleSheetCollection(TreeScope& treeScope)
     43     : m_treeScope(treeScope)
     44     , m_hadActiveLoadingStylesheet(false)
     45     , m_usesRemUnits(false)
     46 {
     47 }
     48 
     49 void TreeScopeStyleSheetCollection::addStyleSheetCandidateNode(Node* node, bool createdByParser)
     50 {
     51     if (!node->inDocument())
     52         return;
     53 
     54     // Until the <body> exists, we have no choice but to compare document positions,
     55     // since styles outside of the body and head continue to be shunted into the head
     56     // (and thus can shift to end up before dynamically added DOM content that is also
     57     // outside the body).
     58     if (createdByParser && document().body())
     59         m_styleSheetCandidateNodes.parserAdd(node);
     60     else
     61         m_styleSheetCandidateNodes.add(node);
     62 }
     63 
     64 void TreeScopeStyleSheetCollection::removeStyleSheetCandidateNode(Node* node, ContainerNode* scopingNode)
     65 {
     66     m_styleSheetCandidateNodes.remove(node);
     67 }
     68 
     69 TreeScopeStyleSheetCollection::StyleResolverUpdateType TreeScopeStyleSheetCollection::compareStyleSheets(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& oldStyleSheets, const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStylesheets, WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& addedSheets)
     70 {
     71     unsigned newStyleSheetCount = newStylesheets.size();
     72     unsigned oldStyleSheetCount = oldStyleSheets.size();
     73     ASSERT(newStyleSheetCount >= oldStyleSheetCount);
     74 
     75     if (!newStyleSheetCount)
     76         return Reconstruct;
     77 
     78     unsigned newIndex = 0;
     79     for (unsigned oldIndex = 0; oldIndex < oldStyleSheetCount; ++oldIndex) {
     80         while (oldStyleSheets[oldIndex] != newStylesheets[newIndex]) {
     81             addedSheets.append(newStylesheets[newIndex]->contents());
     82             if (++newIndex == newStyleSheetCount)
     83                 return Reconstruct;
     84         }
     85         if (++newIndex == newStyleSheetCount)
     86             return Reconstruct;
     87     }
     88     bool hasInsertions = !addedSheets.isEmpty();
     89     while (newIndex < newStyleSheetCount) {
     90         addedSheets.append(newStylesheets[newIndex]->contents());
     91         ++newIndex;
     92     }
     93     // If all new sheets were added at the end of the list we can just add them to existing StyleResolver.
     94     // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly.
     95     return hasInsertions ? Reset : Additive;
     96 }
     97 
     98 bool TreeScopeStyleSheetCollection::activeLoadingStyleSheetLoaded(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStyleSheets)
     99 {
    100     // StyleSheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
    101     bool hasActiveLoadingStylesheet = false;
    102     unsigned newStylesheetCount = newStyleSheets.size();
    103     for (unsigned i = 0; i < newStylesheetCount; ++i) {
    104         if (newStyleSheets[i]->isLoading())
    105             hasActiveLoadingStylesheet = true;
    106     }
    107     if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
    108         m_hadActiveLoadingStylesheet = false;
    109         return true;
    110     }
    111     m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
    112     return false;
    113 }
    114 
    115 static bool findFontFaceRulesFromStyleSheetContents(const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& sheets, WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >& fontFaceRules)
    116 {
    117     bool hasFontFaceRule = false;
    118 
    119     for (unsigned i = 0; i < sheets.size(); ++i) {
    120         ASSERT(sheets[i]);
    121         if (sheets[i]->hasFontFaceRule()) {
    122             // FIXME: We don't need this for styles in shadow tree.
    123             sheets[i]->findFontFaceRules(fontFaceRules);
    124             hasFontFaceRule = true;
    125         }
    126     }
    127     return hasFontFaceRule;
    128 }
    129 
    130 void TreeScopeStyleSheetCollection::analyzeStyleSheetChange(StyleResolverUpdateMode updateMode, const StyleSheetCollection& newCollection, StyleSheetChange& change)
    131 {
    132     if (activeLoadingStyleSheetLoaded(newCollection.activeAuthorStyleSheets()))
    133         return;
    134 
    135     if (updateMode != AnalyzedStyleUpdate)
    136         return;
    137 
    138     // Find out which stylesheets are new.
    139     WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> > addedSheets;
    140     if (m_activeAuthorStyleSheets.size() <= newCollection.activeAuthorStyleSheets().size()) {
    141         change.styleResolverUpdateType = compareStyleSheets(m_activeAuthorStyleSheets, newCollection.activeAuthorStyleSheets(), addedSheets);
    142     } else {
    143         StyleResolverUpdateType updateType = compareStyleSheets(newCollection.activeAuthorStyleSheets(), m_activeAuthorStyleSheets, addedSheets);
    144         if (updateType != Additive) {
    145             change.styleResolverUpdateType = updateType;
    146         } else {
    147             change.styleResolverUpdateType = Reset;
    148             // If @font-face is removed, needs full style recalc.
    149             if (findFontFaceRulesFromStyleSheetContents(addedSheets, change.fontFaceRulesToRemove))
    150                 return;
    151         }
    152     }
    153 
    154     // FIXME: If styleResolverUpdateType is Reconstruct, we should return early here since
    155     // we need to recalc the whole document. It's wrong to use StyleSheetInvalidationAnalysis since
    156     // it only looks at the addedSheets.
    157 
    158     // No point in doing the analysis work if we're just going to recalc the whole document anyways.
    159     // This needs to be done after the compareStyleSheets calls above to ensure we don't throw away
    160     // the StyleResolver if we don't need to.
    161     if (document().hasPendingForcedStyleRecalc())
    162         return;
    163 
    164     // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
    165     if (!document().body() || document().hasNodesWithPlaceholderStyle())
    166         return;
    167     StyleSheetInvalidationAnalysis invalidationAnalysis(addedSheets);
    168     if (invalidationAnalysis.dirtiesAllStyle())
    169         return;
    170     invalidationAnalysis.invalidateStyle(document());
    171     change.requiresFullStyleRecalc = false;
    172     return;
    173 }
    174 
    175 void TreeScopeStyleSheetCollection::clearMediaQueryRuleSetStyleSheets()
    176 {
    177     for (size_t i = 0; i < m_activeAuthorStyleSheets.size(); ++i) {
    178         StyleSheetContents* contents = m_activeAuthorStyleSheets[i]->contents();
    179         if (contents->hasMediaQueries())
    180             contents->clearRuleSet();
    181     }
    182 }
    183 
    184 void TreeScopeStyleSheetCollection::enableExitTransitionStylesheets()
    185 {
    186     DocumentOrderedList::iterator begin = m_styleSheetCandidateNodes.begin();
    187     DocumentOrderedList::iterator end = m_styleSheetCandidateNodes.end();
    188     for (DocumentOrderedList::iterator it = begin; it != end; ++it) {
    189         Node* node = *it;
    190         if (isHTMLLinkElement(*node))
    191             toHTMLLinkElement(node)->enableIfExitTransitionStyle();
    192     }
    193 }
    194 
    195 static bool styleSheetsUseRemUnits(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& sheets)
    196 {
    197     for (unsigned i = 0; i < sheets.size(); ++i) {
    198         if (sheets[i]->contents()->usesRemUnits())
    199             return true;
    200     }
    201     return false;
    202 }
    203 
    204 void TreeScopeStyleSheetCollection::updateUsesRemUnits()
    205 {
    206     m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets);
    207 }
    208 
    209 void TreeScopeStyleSheetCollection::trace(Visitor* visitor)
    210 {
    211     visitor->trace(m_treeScope);
    212     visitor->trace(m_styleSheetCandidateNodes);
    213     StyleSheetCollection::trace(visitor);
    214 }
    215 
    216 }
    217