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 WebCore {
     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     if (!isHTMLStyleElement(*node))
     64         return;
     65 
     66     ContainerNode* scopingNode = toHTMLStyleElement(*node).scopingNode();
     67     if (!isTreeScopeRoot(scopingNode))
     68         m_scopingNodesForStyleScoped.add(scopingNode);
     69 }
     70 
     71 void TreeScopeStyleSheetCollection::removeStyleSheetCandidateNode(Node* node, ContainerNode* scopingNode)
     72 {
     73     m_styleSheetCandidateNodes.remove(node);
     74 
     75     if (!isTreeScopeRoot(scopingNode))
     76         m_scopingNodesForStyleScoped.remove(scopingNode);
     77 }
     78 
     79 TreeScopeStyleSheetCollection::StyleResolverUpdateType TreeScopeStyleSheetCollection::compareStyleSheets(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& oldStyleSheets, const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStylesheets, WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& addedSheets)
     80 {
     81     unsigned newStyleSheetCount = newStylesheets.size();
     82     unsigned oldStyleSheetCount = oldStyleSheets.size();
     83     ASSERT(newStyleSheetCount >= oldStyleSheetCount);
     84 
     85     if (!newStyleSheetCount)
     86         return Reconstruct;
     87 
     88     unsigned newIndex = 0;
     89     for (unsigned oldIndex = 0; oldIndex < oldStyleSheetCount; ++oldIndex) {
     90         while (oldStyleSheets[oldIndex] != newStylesheets[newIndex]) {
     91             addedSheets.append(newStylesheets[newIndex]->contents());
     92             if (++newIndex == newStyleSheetCount)
     93                 return Reconstruct;
     94         }
     95         if (++newIndex == newStyleSheetCount)
     96             return Reconstruct;
     97     }
     98     bool hasInsertions = !addedSheets.isEmpty();
     99     while (newIndex < newStyleSheetCount) {
    100         addedSheets.append(newStylesheets[newIndex]->contents());
    101         ++newIndex;
    102     }
    103     // If all new sheets were added at the end of the list we can just add them to existing StyleResolver.
    104     // If there were insertions we need to re-add all the stylesheets so rules are ordered correctly.
    105     return hasInsertions ? Reset : Additive;
    106 }
    107 
    108 bool TreeScopeStyleSheetCollection::activeLoadingStyleSheetLoaded(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& newStyleSheets)
    109 {
    110     // StyleSheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
    111     bool hasActiveLoadingStylesheet = false;
    112     unsigned newStylesheetCount = newStyleSheets.size();
    113     for (unsigned i = 0; i < newStylesheetCount; ++i) {
    114         if (newStyleSheets[i]->isLoading())
    115             hasActiveLoadingStylesheet = true;
    116     }
    117     if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
    118         m_hadActiveLoadingStylesheet = false;
    119         return true;
    120     }
    121     m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
    122     return false;
    123 }
    124 
    125 static bool findFontFaceRulesFromStyleSheetContents(const WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> >& sheets, WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >& fontFaceRules)
    126 {
    127     bool hasFontFaceRule = false;
    128 
    129     for (unsigned i = 0; i < sheets.size(); ++i) {
    130         ASSERT(sheets[i]);
    131         if (sheets[i]->hasFontFaceRule()) {
    132             // FIXME: We don't need this for styles in shadow tree.
    133             sheets[i]->findFontFaceRules(fontFaceRules);
    134             hasFontFaceRule = true;
    135         }
    136     }
    137     return hasFontFaceRule;
    138 }
    139 
    140 void TreeScopeStyleSheetCollection::analyzeStyleSheetChange(StyleResolverUpdateMode updateMode, const StyleSheetCollection& newCollection, StyleSheetChange& change)
    141 {
    142     if (activeLoadingStyleSheetLoaded(newCollection.activeAuthorStyleSheets()))
    143         return;
    144 
    145     if (updateMode != AnalyzedStyleUpdate)
    146         return;
    147 
    148     // Find out which stylesheets are new.
    149     WillBeHeapVector<RawPtrWillBeMember<StyleSheetContents> > addedSheets;
    150     if (m_activeAuthorStyleSheets.size() <= newCollection.activeAuthorStyleSheets().size()) {
    151         change.styleResolverUpdateType = compareStyleSheets(m_activeAuthorStyleSheets, newCollection.activeAuthorStyleSheets(), addedSheets);
    152     } else {
    153         StyleResolverUpdateType updateType = compareStyleSheets(newCollection.activeAuthorStyleSheets(), m_activeAuthorStyleSheets, addedSheets);
    154         if (updateType != Additive) {
    155             change.styleResolverUpdateType = updateType;
    156         } else {
    157             change.styleResolverUpdateType = Reset;
    158             // If @font-face is removed, needs full style recalc.
    159             if (findFontFaceRulesFromStyleSheetContents(addedSheets, change.fontFaceRulesToRemove))
    160                 return;
    161         }
    162     }
    163 
    164     // FIXME: If styleResolverUpdateType is Reconstruct, we should return early here since
    165     // we need to recalc the whole document. It's wrong to use StyleSheetInvalidationAnalysis since
    166     // it only looks at the addedSheets.
    167 
    168     // No point in doing the analysis work if we're just going to recalc the whole document anyways.
    169     // This needs to be done after the compareStyleSheets calls above to ensure we don't throw away
    170     // the StyleResolver if we don't need to.
    171     if (document().hasPendingForcedStyleRecalc())
    172         return;
    173 
    174     // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
    175     if (!document().body() || document().hasNodesWithPlaceholderStyle())
    176         return;
    177     StyleSheetInvalidationAnalysis invalidationAnalysis(addedSheets);
    178     if (invalidationAnalysis.dirtiesAllStyle())
    179         return;
    180     invalidationAnalysis.invalidateStyle(document());
    181     change.requiresFullStyleRecalc = false;
    182     return;
    183 }
    184 
    185 void TreeScopeStyleSheetCollection::clearMediaQueryRuleSetStyleSheets()
    186 {
    187     for (size_t i = 0; i < m_activeAuthorStyleSheets.size(); ++i) {
    188         StyleSheetContents* contents = m_activeAuthorStyleSheets[i]->contents();
    189         if (contents->hasMediaQueries())
    190             contents->clearRuleSet();
    191     }
    192 }
    193 
    194 void TreeScopeStyleSheetCollection::enableExitTransitionStylesheets()
    195 {
    196     DocumentOrderedList::iterator begin = m_styleSheetCandidateNodes.begin();
    197     DocumentOrderedList::iterator end = m_styleSheetCandidateNodes.end();
    198     for (DocumentOrderedList::iterator it = begin; it != end; ++it) {
    199         Node* node = *it;
    200         if (isHTMLLinkElement(*node))
    201             toHTMLLinkElement(node)->enableIfExitTransitionStyle();
    202     }
    203 }
    204 
    205 void TreeScopeStyleSheetCollection::resetAllRuleSetsInTreeScope(StyleResolver* styleResolver)
    206 {
    207     // FIXME: If many web developers use style scoped, implement reset RuleSets in per-scoping node manner.
    208     if (DocumentOrderedList* styleScopedScopingNodes = scopingNodesForStyleScoped()) {
    209         for (DocumentOrderedList::iterator it = styleScopedScopingNodes->begin(); it != styleScopedScopingNodes->end(); ++it)
    210             styleResolver->resetAuthorStyle(toContainerNode(*it));
    211     }
    212     if (ListHashSet<Node*, 4>* removedNodes = scopingNodesRemoved()) {
    213         for (ListHashSet<Node*, 4>::iterator it = removedNodes->begin(); it != removedNodes->end(); ++it)
    214             styleResolver->resetAuthorStyle(toContainerNode(*it));
    215     }
    216     styleResolver->resetAuthorStyle(toContainerNode(&m_treeScope.rootNode()));
    217 }
    218 
    219 static bool styleSheetsUseRemUnits(const WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> >& sheets)
    220 {
    221     for (unsigned i = 0; i < sheets.size(); ++i) {
    222         if (sheets[i]->contents()->usesRemUnits())
    223             return true;
    224     }
    225     return false;
    226 }
    227 
    228 void TreeScopeStyleSheetCollection::updateUsesRemUnits()
    229 {
    230     m_usesRemUnits = styleSheetsUseRemUnits(m_activeAuthorStyleSheets);
    231 }
    232 
    233 }
    234