Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / css / StyleSheetContents.cpp
index 366c399..5b72384 100644 (file)
@@ -27,7 +27,7 @@
 #include "core/css/StylePropertySet.h"
 #include "core/css/StyleRule.h"
 #include "core/css/StyleRuleImport.h"
-#include "core/css/resolver/StyleResolver.h"
+#include "core/dom/Document.h"
 #include "core/dom/Node.h"
 #include "core/dom/StyleEngine.h"
 #include "core/fetch/CSSStyleSheetResource.h"
@@ -45,7 +45,7 @@ unsigned StyleSheetContents::estimatedSizeInBytes() const
     // The assumption is that nearly all of of them are atomic and would exist anyway.
     unsigned size = sizeof(*this);
 
-    // FIXME: This ignores the children of media and region rules.
+    // FIXME: This ignores the children of media rules.
     // Most rules are StyleRules.
     size += ruleCount() * StyleRule::averageSizeInBytes();
 
@@ -66,13 +66,13 @@ StyleSheetContents::StyleSheetContents(StyleRuleImport* ownerRule, const String&
     , m_isInMemoryCache(false)
     , m_hasFontFaceRule(false)
     , m_hasMediaQueries(false)
+    , m_hasSingleOwnerDocument(true)
     , m_parserContext(context)
 {
 }
 
 StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
-    : RefCounted<StyleSheetContents>()
-    , m_ownerRule(0)
+    : m_ownerRule(nullptr)
     , m_originalURL(o.m_originalURL)
     , m_encodingFromCharsetRule(o.m_encodingFromCharsetRule)
     , m_importRules(o.m_importRules.size())
@@ -85,6 +85,7 @@ StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
     , m_isInMemoryCache(false)
     , m_hasFontFaceRule(o.m_hasFontFaceRule)
     , m_hasMediaQueries(o.m_hasMediaQueries)
+    , m_hasSingleOwnerDocument(true)
     , m_parserContext(o.m_parserContext)
 {
     ASSERT(o.isCacheable());
@@ -98,19 +99,25 @@ StyleSheetContents::StyleSheetContents(const StyleSheetContents& o)
 
 StyleSheetContents::~StyleSheetContents()
 {
-    StyleEngine::removeSheet(this);
+#if !ENABLE(OILPAN)
     clearRules();
+#endif
 }
 
 void StyleSheetContents::setHasSyntacticallyValidCSSHeader(bool isValidCss)
 {
-    if (maybeCacheable() && !isValidCss)
-        StyleEngine::removeSheet(this);
+    if (!isValidCss) {
+        if (Document* document = clientSingleOwnerDocument())
+            removeSheetFromCache(document);
+    }
     m_hasSyntacticallyValidCSSHeader = isValidCss;
 }
 
-bool StyleSheetContents::maybeCacheable() const
+bool StyleSheetContents::isCacheable() const
 {
+    // This would require dealing with multiple clients for load callbacks.
+    if (!loadCompleted())
+        return false;
     // FIXME: StyleSheets with media queries can't be cached because their RuleSet
     // is processed differently based off the media queries, which might resolve
     // differently depending on the context of the parent CSSStyleSheet (e.g.
@@ -136,15 +143,7 @@ bool StyleSheetContents::maybeCacheable() const
     return true;
 }
 
-bool StyleSheetContents::isCacheable() const
-{
-    // This would require dealing with multiple clients for load callbacks.
-    if (!loadCompleted())
-        return false;
-    return maybeCacheable();
-}
-
-void StyleSheetContents::parserAppendRule(PassRefPtr<StyleRuleBase> rule)
+void StyleSheetContents::parserAppendRule(PassRefPtrWillBeRawPtr<StyleRuleBase> rule)
 {
     ASSERT(!rule->isCharsetRule());
     if (rule->isImportRule()) {
@@ -224,7 +223,7 @@ void StyleSheetContents::parserSetEncodingFromCharsetRule(const String& encoding
     m_encodingFromCharsetRule = encoding;
 }
 
-bool StyleSheetContents::wrapperInsertRule(PassRefPtr<StyleRuleBase> rule, unsigned index)
+bool StyleSheetContents::wrapperInsertRule(PassRefPtrWillBeRawPtr<StyleRuleBase> rule, unsigned index)
 {
     ASSERT(m_isMutable);
     ASSERT_WITH_SECURITY_IMPLICATION(index <= ruleCount());
@@ -265,6 +264,8 @@ bool StyleSheetContents::wrapperInsertRule(PassRefPtr<StyleRuleBase> rule, unsig
 
     childVectorIndex -= m_importRules.size();
 
+    if (rule->isFontFaceRule())
+        setHasFontFaceRule(true);
     m_childRules.insert(childVectorIndex, rule);
     return true;
 }
@@ -284,11 +285,15 @@ void StyleSheetContents::wrapperDeleteRule(unsigned index)
     }
     if (childVectorIndex < m_importRules.size()) {
         m_importRules[childVectorIndex]->clearParentStyleSheet();
+        if (m_importRules[childVectorIndex]->isFontFaceRule())
+            notifyRemoveFontFaceRule(toStyleRuleFontFace(m_importRules[childVectorIndex].get()));
         m_importRules.remove(childVectorIndex);
         return;
     }
     childVectorIndex -= m_importRules.size();
 
+    if (m_childRules[childVectorIndex]->isFontFaceRule())
+        notifyRemoveFontFaceRule(toStyleRuleFontFace(m_childRules[childVectorIndex].get()));
     m_childRules.remove(childVectorIndex);
 }
 
@@ -299,7 +304,7 @@ void StyleSheetContents::parserAddNamespace(const AtomicString& prefix, const At
     PrefixNamespaceURIMap::AddResult result = m_namespaces.add(prefix, uri);
     if (result.isNewEntry)
         return;
-    result.iterator->value = uri;
+    result.storedValue->value = uri;
 }
 
 const AtomicString& StyleSheetContents::determineNamespace(const AtomicString& prefix)
@@ -321,7 +326,8 @@ void StyleSheetContents::parseAuthorStyleSheet(const CSSStyleSheetResource* cach
     bool hasValidMIMEType = false;
     String sheetText = cachedStyleSheet->sheetText(enforceMIMEType, &hasValidMIMEType);
 
-    BisonCSSParser p(parserContext(), UseCounter::getFrom(this));
+    CSSParserContext context(parserContext(), UseCounter::getFrom(this));
+    BisonCSSParser p(context);
     p.parseSheet(this, sheetText, TextPosition::minimumPosition(), 0, true);
 
     // If we're loading a stylesheet cross-origin, and the MIME type is not standard, require the CSS
@@ -343,7 +349,8 @@ bool StyleSheetContents::parseString(const String& sheetText)
 
 bool StyleSheetContents::parseStringAtPosition(const String& sheetText, const TextPosition& startPosition, bool createdByParser)
 {
-    BisonCSSParser p(parserContext(), UseCounter::getFrom(this));
+    CSSParserContext context(parserContext(), UseCounter::getFrom(this));
+    BisonCSSParser p(context);
     p.parseSheet(this, sheetText, startPosition, 0, createdByParser);
 
     return true;
@@ -365,11 +372,7 @@ bool StyleSheetContents::loadCompleted() const
         return parentSheet->loadCompleted();
 
     StyleSheetContents* root = rootStyleSheet();
-    for (unsigned i = 0; i < root->m_clients.size(); ++i) {
-        if (!root->m_clients[i]->loadCompleted())
-            return false;
-    }
-    return true;
+    return root->m_loadingClients.isEmpty();
 }
 
 void StyleSheetContents::checkLoaded()
@@ -380,7 +383,7 @@ void StyleSheetContents::checkLoaded()
     // Avoid |this| being deleted by scripts that run via
     // ScriptableDocumentParser::executeScriptsWaitingForResources().
     // See https://bugs.webkit.org/show_bug.cgi?id=95106
-    RefPtr<StyleSheetContents> protect(this);
+    RefPtrWillBeRawPtr<StyleSheetContents> protect(this);
 
     StyleSheetContents* parentSheet = parentStyleSheet();
     if (parentSheet) {
@@ -389,21 +392,29 @@ void StyleSheetContents::checkLoaded()
     }
 
     StyleSheetContents* root = rootStyleSheet();
-    if (root->m_clients.isEmpty())
+    if (root->m_loadingClients.isEmpty())
         return;
 
-    Vector<CSSStyleSheet*> clients(root->m_clients);
-    for (unsigned i = 0; i < clients.size(); ++i) {
-        // Avoid |CSSSStyleSheet| and |ownerNode| being deleted by scripts that run via
-        // ScriptableDocumentParser::executeScriptsWaitingForResources().
-        RefPtr<CSSStyleSheet> protectClient(clients[i]);
-
-        if (clients[i]->loadCompleted())
+    // Avoid |CSSSStyleSheet| and |ownerNode| being deleted by scripts that run via
+    // ScriptableDocumentParser::executeScriptsWaitingForResources(). Also protect
+    // the |CSSStyleSheet| from being deleted during iteration via the |sheetLoaded|
+    // method.
+    //
+    // When a sheet is loaded it is moved from the set of loading clients
+    // to the set of completed clients. We therefore need the copy in order to
+    // not modify the set while iterating it.
+    WillBeHeapVector<RefPtrWillBeMember<CSSStyleSheet> > loadingClients;
+    copyToVector(m_loadingClients, loadingClients);
+
+    for (unsigned i = 0; i < loadingClients.size(); ++i) {
+        if (loadingClients[i]->loadCompleted())
             continue;
 
-        RefPtr<Node> ownerNode = clients[i]->ownerNode();
-        if (clients[i]->sheetLoaded())
-            ownerNode->notifyLoadedSheetAndAllCriticalSubresources(m_didLoadErrorOccur);
+        // sheetLoaded might be invoked after its owner node is removed from document.
+        if (RefPtr<Node> ownerNode = loadingClients[i]->ownerNode()) {
+            if (loadingClients[i]->sheetLoaded())
+                ownerNode->notifyLoadedSheetAndAllCriticalSubresources(m_didLoadErrorOccur);
+        }
     }
 }
 
@@ -420,8 +431,17 @@ void StyleSheetContents::notifyLoadedSheet(const CSSStyleSheetResource* sheet)
 void StyleSheetContents::startLoadingDynamicSheet()
 {
     StyleSheetContents* root = rootStyleSheet();
-    for (unsigned i = 0; i < root->m_clients.size(); ++i)
-        root->m_clients[i]->startLoadingDynamicSheet();
+    for (ClientsIterator it = root->m_loadingClients.begin(); it != root->m_loadingClients.end(); ++it)
+        (*it)->startLoadingDynamicSheet();
+    // Copy the completed clients to a vector for iteration.
+    // startLoadingDynamicSheet will move the style sheet from the
+    // completed state to the loading state which modifies the set of
+    // completed clients. We therefore need the copy in order to not
+    // modify the set of completed clients while iterating it.
+    WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > completedClients;
+    copyToVector(root->m_completedClients, completedClients);
+    for (unsigned i = 0; i < completedClients.size(); ++i)
+        completedClients[i]->startLoadingDynamicSheet();
 }
 
 StyleSheetContents* StyleSheetContents::rootStyleSheet() const
@@ -434,25 +454,23 @@ StyleSheetContents* StyleSheetContents::rootStyleSheet() const
 
 bool StyleSheetContents::hasSingleOwnerNode() const
 {
-    StyleSheetContents* root = rootStyleSheet();
-    if (root->m_clients.isEmpty())
-        return false;
-    return root->m_clients.size() == 1;
+    return rootStyleSheet()->hasOneClient();
 }
 
 Node* StyleSheetContents::singleOwnerNode() const
 {
     StyleSheetContents* root = rootStyleSheet();
-    if (root->m_clients.isEmpty())
+    if (!root->hasOneClient())
         return 0;
-    ASSERT(root->m_clients.size() == 1);
-    return root->m_clients[0]->ownerNode();
+    if (root->m_loadingClients.size())
+        return (*root->m_loadingClients.begin())->ownerNode();
+    return (*root->m_completedClients.begin())->ownerNode();
 }
 
 Document* StyleSheetContents::singleOwnerDocument() const
 {
-    Node* ownerNode = singleOwnerNode();
-    return ownerNode ? &ownerNode->document() : 0;
+    StyleSheetContents* root = rootStyleSheet();
+    return root->clientSingleOwnerDocument();
 }
 
 KURL StyleSheetContents::completeURL(const String& url) const
@@ -461,27 +479,23 @@ KURL StyleSheetContents::completeURL(const String& url) const
     return m_parserContext.completeURL(url);
 }
 
-static bool childRulesHaveFailedOrCanceledSubresources(const Vector<RefPtr<StyleRuleBase> >& rules)
+static bool childRulesHaveFailedOrCanceledSubresources(const WillBeHeapVector<RefPtrWillBeMember<StyleRuleBase> >& rules)
 {
     for (unsigned i = 0; i < rules.size(); ++i) {
         const StyleRuleBase* rule = rules[i].get();
         switch (rule->type()) {
         case StyleRuleBase::Style:
-            if (toStyleRule(rule)->properties()->hasFailedOrCanceledSubresources())
+            if (toStyleRule(rule)->properties().hasFailedOrCanceledSubresources())
                 return true;
             break;
         case StyleRuleBase::FontFace:
-            if (toStyleRuleFontFace(rule)->properties()->hasFailedOrCanceledSubresources())
+            if (toStyleRuleFontFace(rule)->properties().hasFailedOrCanceledSubresources())
                 return true;
             break;
         case StyleRuleBase::Media:
             if (childRulesHaveFailedOrCanceledSubresources(toStyleRuleMedia(rule)->childRules()))
                 return true;
             break;
-        case StyleRuleBase::Region:
-            if (childRulesHaveFailedOrCanceledSubresources(toStyleRuleRegion(rule)->childRules()))
-                return true;
-            break;
         case StyleRuleBase::Import:
             ASSERT_NOT_REACHED();
         case StyleRuleBase::Page:
@@ -504,6 +518,16 @@ bool StyleSheetContents::hasFailedOrCanceledSubresources() const
     return childRulesHaveFailedOrCanceledSubresources(m_childRules);
 }
 
+Document* StyleSheetContents::clientSingleOwnerDocument() const
+{
+    if (!m_hasSingleOwnerDocument || clientSize() <= 0)
+        return 0;
+
+    if (m_loadingClients.size())
+        return (*m_loadingClients.begin())->ownerDocument();
+    return (*m_completedClients.begin())->ownerDocument();
+}
+
 StyleSheetContents* StyleSheetContents::parentStyleSheet() const
 {
     return m_ownerRule ? m_ownerRule->parentStyleSheet() : 0;
@@ -511,15 +535,55 @@ StyleSheetContents* StyleSheetContents::parentStyleSheet() const
 
 void StyleSheetContents::registerClient(CSSStyleSheet* sheet)
 {
-    ASSERT(!m_clients.contains(sheet));
-    m_clients.append(sheet);
+    ASSERT(!m_loadingClients.contains(sheet) && !m_completedClients.contains(sheet));
+
+    // InspectorCSSAgent::buildObjectForRule creates CSSStyleSheet without any owner node.
+    if (!sheet->ownerDocument())
+        return;
+
+    if (Document* document = clientSingleOwnerDocument()) {
+        if (sheet->ownerDocument() != document)
+            m_hasSingleOwnerDocument = false;
+    }
+    m_loadingClients.add(sheet);
 }
 
 void StyleSheetContents::unregisterClient(CSSStyleSheet* sheet)
 {
-    size_t position = m_clients.find(sheet);
-    ASSERT(position != kNotFound);
-    m_clients.remove(position);
+    m_loadingClients.remove(sheet);
+    m_completedClients.remove(sheet);
+
+    if (!sheet->ownerDocument() || !m_loadingClients.isEmpty() || !m_completedClients.isEmpty())
+        return;
+
+    if (m_hasSingleOwnerDocument)
+        removeSheetFromCache(sheet->ownerDocument());
+    m_hasSingleOwnerDocument = true;
+}
+
+void StyleSheetContents::clientLoadCompleted(CSSStyleSheet* sheet)
+{
+    ASSERT(m_loadingClients.contains(sheet) || !sheet->ownerDocument());
+    m_loadingClients.remove(sheet);
+    // In m_ownerNode->sheetLoaded, the CSSStyleSheet might be detached.
+    // (i.e. clearOwnerNode was invoked.)
+    // In this case, we don't need to add the stylesheet to completed clients.
+    if (!sheet->ownerDocument())
+        return;
+    m_completedClients.add(sheet);
+}
+
+void StyleSheetContents::clientLoadStarted(CSSStyleSheet* sheet)
+{
+    ASSERT(m_completedClients.contains(sheet));
+    m_completedClients.remove(sheet);
+    m_loadingClients.add(sheet);
+}
+
+void StyleSheetContents::removeSheetFromCache(Document* document)
+{
+    ASSERT(document);
+    document->styleEngine()->removeSheet(this);
 }
 
 void StyleSheetContents::addedToMemoryCache()
@@ -551,6 +615,14 @@ RuleSet& StyleSheetContents::ensureRuleSet(const MediaQueryEvaluator& medium, Ad
     return *m_ruleSet.get();
 }
 
+static void clearResolvers(WillBeHeapHashSet<RawPtrWillBeWeakMember<CSSStyleSheet> >& clients)
+{
+    for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CSSStyleSheet> >::iterator it = clients.begin(); it != clients.end(); ++it) {
+        if (Document* document = (*it)->ownerDocument())
+            document->styleEngine()->clearResolver();
+    }
+}
+
 void StyleSheetContents::clearRuleSet()
 {
     if (StyleSheetContents* parentSheet = parentStyleSheet())
@@ -564,12 +636,61 @@ void StyleSheetContents::clearRuleSet()
 
     // Clearing the ruleSet means we need to recreate the styleResolver data structures.
     // See the StyleResolver calls in ScopedStyleResolver::addRulesFromSheet.
-    for (size_t i = 0; i < m_clients.size(); ++i) {
-        if (Document* document = m_clients[i]->ownerDocument())
-            document->styleEngine()->clearResolver();
-    }
+    clearResolvers(m_loadingClients);
+    clearResolvers(m_completedClients);
     m_ruleSet.clear();
 }
 
+static void removeFontFaceRules(WillBeHeapHashSet<RawPtrWillBeWeakMember<CSSStyleSheet> >& clients, const StyleRuleFontFace* fontFaceRule)
+{
+    for (WillBeHeapHashSet<RawPtrWillBeWeakMember<CSSStyleSheet> >::iterator it = clients.begin(); it != clients.end(); ++it) {
+        if (Node* ownerNode = (*it)->ownerNode())
+            ownerNode->document().styleEngine()->removeFontFaceRules(WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >(1, fontFaceRule));
+    }
+}
+
+void StyleSheetContents::notifyRemoveFontFaceRule(const StyleRuleFontFace* fontFaceRule)
+{
+    StyleSheetContents* root = rootStyleSheet();
+    removeFontFaceRules(root->m_loadingClients, fontFaceRule);
+    removeFontFaceRules(root->m_completedClients, fontFaceRule);
+}
+
+static void findFontFaceRulesFromRules(const WillBeHeapVector<RefPtrWillBeMember<StyleRuleBase> >& rules, WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >& fontFaceRules)
+{
+    for (unsigned i = 0; i < rules.size(); ++i) {
+        StyleRuleBase* rule = rules[i].get();
+
+        if (rule->isFontFaceRule()) {
+            fontFaceRules.append(toStyleRuleFontFace(rule));
+        } else if (rule->isMediaRule()) {
+            StyleRuleMedia* mediaRule = toStyleRuleMedia(rule);
+            // We cannot know whether the media rule matches or not, but
+            // for safety, remove @font-face in the media rule (if exists).
+            findFontFaceRulesFromRules(mediaRule->childRules(), fontFaceRules);
+        }
+    }
+}
+
+void StyleSheetContents::findFontFaceRules(WillBeHeapVector<RawPtrWillBeMember<const StyleRuleFontFace> >& fontFaceRules)
+{
+    for (unsigned i = 0; i < m_importRules.size(); ++i) {
+        if (!m_importRules[i]->styleSheet())
+            continue;
+        m_importRules[i]->styleSheet()->findFontFaceRules(fontFaceRules);
+    }
+
+    findFontFaceRulesFromRules(childRules(), fontFaceRules);
+}
+
+void StyleSheetContents::trace(Visitor* visitor)
+{
+    visitor->trace(m_ownerRule);
+    visitor->trace(m_importRules);
+    visitor->trace(m_childRules);
+    visitor->trace(m_loadingClients);
+    visitor->trace(m_completedClients);
+    visitor->trace(m_ruleSet);
+}
 
 }