Unreviewed build fix
[WebKit-https.git] / Source / WebCore / contentextensions / ContentExtensionsBackend.cpp
index 80243d2..a798b5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if ENABLE(CONTENT_EXTENSIONS)
 
+#include "Chrome.h"
+#include "ChromeClient.h"
 #include "CompiledContentExtension.h"
 #include "ContentExtension.h"
 #include "ContentExtensionsDebugging.h"
+#include "ContentRuleListResults.h"
+#include "CustomHeaderFields.h"
 #include "DFABytecodeInterpreter.h"
 #include "Document.h"
 #include "DocumentLoader.h"
+#include "ExtensionStyleSheets.h"
 #include "Frame.h"
-#include "MainFrame.h"
+#include "FrameLoaderClient.h"
+#include "Page.h"
 #include "ResourceLoadInfo.h"
-#include "URL.h"
+#include <wtf/URL.h>
 #include "UserContentController.h"
 #include <wtf/NeverDestroyed.h>
 #include <wtf/text/CString.h>
@@ -46,19 +52,14 @@ namespace WebCore {
 
 namespace ContentExtensions {
     
-void ContentExtensionsBackend::addContentExtension(const String& identifier, RefPtr<CompiledContentExtension> compiledContentExtension)
+void ContentExtensionsBackend::addContentExtension(const String& identifier, Ref<CompiledContentExtension> compiledContentExtension, ContentExtension::ShouldCompileCSS shouldCompileCSS)
 {
     ASSERT(!identifier.isEmpty());
     if (identifier.isEmpty())
         return;
-
-    if (!compiledContentExtension) {
-        removeContentExtension(identifier);
-        return;
-    }
-
-    RefPtr<ContentExtension> extension = ContentExtension::create(identifier, adoptRef(*compiledContentExtension.leakRef()));
-    m_contentExtensions.set(identifier, WTF::move(extension));
+    
+    auto contentExtension = ContentExtension::create(identifier, WTFMove(compiledContentExtension), shouldCompileCSS);
+    m_contentExtensions.set(identifier, WTFMove(contentExtension));
 }
 
 void ContentExtensionsBackend::removeContentExtension(const String& identifier)
@@ -71,89 +72,77 @@ void ContentExtensionsBackend::removeAllContentExtensions()
     m_contentExtensions.clear();
 }
 
-Vector<Action> ContentExtensionsBackend::actionsForResourceLoad(const ResourceLoadInfo& resourceLoadInfo) const
+auto ContentExtensionsBackend::actionsForResourceLoad(const ResourceLoadInfo& resourceLoadInfo) const -> Vector<ActionsFromContentRuleList>
 {
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
-    double addedTimeStart = monotonicallyIncreasingTime();
+    MonotonicTime addedTimeStart = MonotonicTime::now();
 #endif
-    if (resourceLoadInfo.resourceURL.protocolIsData())
-        return Vector<Action>();
+    if (m_contentExtensions.isEmpty()
+        || !resourceLoadInfo.resourceURL.isValid()
+        || resourceLoadInfo.resourceURL.protocolIsData())
+        return { };
 
     const String& urlString = resourceLoadInfo.resourceURL.string();
-    ASSERT_WITH_MESSAGE(urlString.containsOnlyASCII(), "A decoded URL should only contain ASCII characters. The matching algorithm assumes the input is ASCII.");
-    const CString& urlCString = urlString.utf8();
+    ASSERT_WITH_MESSAGE(urlString.isAllASCII(), "A decoded URL should only contain ASCII characters. The matching algorithm assumes the input is ASCII.");
+    const auto urlCString = urlString.utf8();
 
-    Vector<Action> finalActions;
-    ResourceFlags flags = resourceLoadInfo.getResourceFlags();
+    Vector<ActionsFromContentRuleList> actionsVector;
+    actionsVector.reserveInitialCapacity(m_contentExtensions.size());
+    const ResourceFlags flags = resourceLoadInfo.getResourceFlags();
     for (auto& contentExtension : m_contentExtensions.values()) {
-        RELEASE_ASSERT(contentExtension);
+        ActionsFromContentRuleList actionsStruct;
+        actionsStruct.contentRuleListIdentifier = contentExtension->identifier();
+
         const CompiledContentExtension& compiledExtension = contentExtension->compiledExtension();
         
-        DFABytecodeInterpreter withoutDomainsInterpreter(compiledExtension.filtersWithoutDomainsBytecode(), compiledExtension.filtersWithoutDomainsBytecodeLength());
-        DFABytecodeInterpreter::Actions triggeredActions = withoutDomainsInterpreter.interpret(urlCString, flags);
+        DFABytecodeInterpreter withoutConditionsInterpreter(compiledExtension.filtersWithoutConditionsBytecode(), compiledExtension.filtersWithoutConditionsBytecodeLength());
+        DFABytecodeInterpreter::Actions withoutConditionsActions = withoutConditionsInterpreter.interpret(urlCString, flags);
         
-        // Check to see if there are any actions triggered with if- or unless-domain and check the domain if there are.
-        DFABytecodeInterpreter withDomainsInterpreter(compiledExtension.filtersWithDomainsBytecode(), compiledExtension.filtersWithDomainsBytecodeLength());
-        
-        DFABytecodeInterpreter::Actions withDomainsPossibleActions = withDomainsInterpreter.interpret(urlCString, flags);
-        if (!withDomainsPossibleActions.isEmpty()) {
-            DFABytecodeInterpreter domainsInterpreter(compiledExtension.domainFiltersBytecode(), compiledExtension.domainFiltersBytecodeLength());
-            DFABytecodeInterpreter::Actions domainsActions = domainsInterpreter.interpret(resourceLoadInfo.mainDocumentURL.host().utf8(), flags);
-            
-            DFABytecodeInterpreter::Actions ifDomainActions;
-            DFABytecodeInterpreter::Actions unlessDomainActions;
-            for (uint64_t action : domainsActions) {
-                if (action & IfDomainFlag)
-                    ifDomainActions.add(action);
-                else
-                    unlessDomainActions.add(action);
-            }
-            
-            for (uint64_t action : withDomainsPossibleActions) {
-                if (ifDomainActions.contains(action)) {
-                    // If an if-domain trigger matches, add the action.
-                    ASSERT(action & IfDomainFlag);
-                    triggeredActions.add(action & ~IfDomainFlag);
-                } else if (!(action & IfDomainFlag) && !unlessDomainActions.contains(action)) {
-                    // If this action did not need an if-domain, it must have been an unless-domain rule.
-                    // Add the action unless it matched an unless-domain trigger.
-                    triggeredActions.add(action);
-                }
-            }
-        }
+        URL topURL = resourceLoadInfo.mainDocumentURL;
+        DFABytecodeInterpreter withConditionsInterpreter(compiledExtension.filtersWithConditionsBytecode(), compiledExtension.filtersWithConditionsBytecodeLength());
+        DFABytecodeInterpreter::Actions withConditionsActions = withConditionsInterpreter.interpretWithConditions(urlCString, flags, contentExtension->topURLActions(topURL));
         
         const SerializedActionByte* actions = compiledExtension.actions();
         const unsigned actionsLength = compiledExtension.actionsLength();
         
-        bool sawIgnorePreviousRules = false;
-        if (!triggeredActions.isEmpty()) {
-            Vector<unsigned> actionLocations;
-            actionLocations.reserveInitialCapacity(triggeredActions.size());
-            for (auto actionLocation : triggeredActions)
-                actionLocations.append(static_cast<unsigned>(actionLocation));
+        const Vector<uint32_t>& universalWithConditions = contentExtension->universalActionsWithConditions(topURL);
+        const Vector<uint32_t>& universalWithoutConditions = contentExtension->universalActionsWithoutConditions();
+        if (!withoutConditionsActions.isEmpty() || !withConditionsActions.isEmpty() || !universalWithConditions.isEmpty() || !universalWithoutConditions.isEmpty()) {
+            Vector<uint32_t> actionLocations;
+            actionLocations.reserveInitialCapacity(withoutConditionsActions.size() + withConditionsActions.size() + universalWithoutConditions.size() + universalWithConditions.size());
+            for (uint64_t actionLocation : withoutConditionsActions)
+                actionLocations.uncheckedAppend(static_cast<uint32_t>(actionLocation));
+            for (uint64_t actionLocation : withConditionsActions)
+                actionLocations.uncheckedAppend(static_cast<uint32_t>(actionLocation));
+            for (uint32_t actionLocation : universalWithoutConditions)
+                actionLocations.uncheckedAppend(actionLocation);
+            for (uint32_t actionLocation : universalWithConditions)
+                actionLocations.uncheckedAppend(actionLocation);
             std::sort(actionLocations.begin(), actionLocations.end());
 
             // Add actions in reverse order to properly deal with IgnorePreviousRules.
             for (unsigned i = actionLocations.size(); i; i--) {
                 Action action = Action::deserialize(actions, actionsLength, actionLocations[i - 1]);
-                action.setExtensionIdentifier(contentExtension->identifier());
                 if (action.type() == ActionType::IgnorePreviousRules) {
-                    sawIgnorePreviousRules = true;
+                    actionsStruct.sawIgnorePreviousRules = true;
                     break;
                 }
-                finalActions.append(action);
+                actionsStruct.actions.append(WTFMove(action));
             }
         }
-        if (!sawIgnorePreviousRules) {
-            finalActions.append(Action(ActionType::CSSDisplayNoneStyleSheet, contentExtension->identifier()));
-            finalActions.last().setExtensionIdentifier(contentExtension->identifier());
-        }
+        actionsVector.uncheckedAppend(WTFMove(actionsStruct));
     }
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
-    double addedTimeEnd = monotonicallyIncreasingTime();
-    WTFLogAlways("Time added: %f microseconds %s", (addedTimeEnd - addedTimeStart) * 1.0e6, resourceLoadInfo.resourceURL.string().utf8().data());
+    MonotonicTime addedTimeEnd = MonotonicTime::now();
+    dataLogF("Time added: %f microseconds %s \n", (addedTimeEnd - addedTimeStart).microseconds(), resourceLoadInfo.resourceURL.string().utf8().data());
 #endif
-    return finalActions;
+    return actionsVector;
+}
+
+void ContentExtensionsBackend::forEach(const WTF::Function<void(const String&, ContentExtension&)>& apply)
+{
+    for (auto& pair : m_contentExtensions)
+        apply(pair.key, pair.value);
 }
 
 StyleSheetContents* ContentExtensionsBackend::globalDisplayNoneStyleSheet(const String& identifier) const
@@ -162,66 +151,157 @@ StyleSheetContents* ContentExtensionsBackend::globalDisplayNoneStyleSheet(const
     return contentExtension ? contentExtension->globalDisplayNoneStyleSheet() : nullptr;
 }
 
-void ContentExtensionsBackend::processContentExtensionRulesForLoad(ResourceRequest& request, ResourceType resourceType, DocumentLoader& initiatingDocumentLoader)
+ContentRuleListResults ContentExtensionsBackend::processContentRuleListsForLoad(const URL& url, OptionSet<ResourceType> resourceType, DocumentLoader& initiatingDocumentLoader)
 {
+    if (m_contentExtensions.isEmpty())
+        return { };
+
     Document* currentDocument = nullptr;
     URL mainDocumentURL;
 
-    if (initiatingDocumentLoader.frame()) {
-        currentDocument = initiatingDocumentLoader.frame()->document();
+    if (Frame* frame = initiatingDocumentLoader.frame()) {
+        currentDocument = frame->document();
 
-        if (Document* mainDocument = initiatingDocumentLoader.frame()->mainFrame().document())
+        if (initiatingDocumentLoader.isLoadingMainResource()
+            && frame->isMainFrame()
+            && resourceType == ResourceType::Document)
+            mainDocumentURL = url;
+        else if (Document* mainDocument = frame->mainFrame().document())
             mainDocumentURL = mainDocument->url();
     }
 
-    ResourceLoadInfo resourceLoadInfo = { request.url(), mainDocumentURL, resourceType };
-    Vector<ContentExtensions::Action> actions = actionsForResourceLoad(resourceLoadInfo);
-
-    StringBuilder css;
-    bool willBlockLoad = false;
-    for (const auto& action : actions) {
-        switch (action.type()) {
-        case ContentExtensions::ActionType::BlockLoad:
-            willBlockLoad = true;
-            break;
-        case ContentExtensions::ActionType::BlockCookies:
-            request.setAllowCookies(false);
-            break;
-        case ContentExtensions::ActionType::CSSDisplayNoneSelector:
-            if (resourceType == ResourceType::Document)
-                initiatingDocumentLoader.addPendingContentExtensionDisplayNoneSelector(action.extensionIdentifier(), action.stringArgument(), action.actionID());
-            else if (currentDocument)
-                currentDocument->styleSheetCollection().addDisplayNoneSelector(action.extensionIdentifier(), action.stringArgument(), action.actionID());
-            break;
-        case ContentExtensions::ActionType::CSSDisplayNoneStyleSheet: {
-            StyleSheetContents* styleSheetContents = globalDisplayNoneStyleSheet(action.stringArgument());
-            if (styleSheetContents) {
+    ResourceLoadInfo resourceLoadInfo = { url, mainDocumentURL, resourceType };
+    auto actions = actionsForResourceLoad(resourceLoadInfo);
+
+    ContentRuleListResults results;
+    results.results.reserveInitialCapacity(actions.size());
+    for (const auto& actionsFromContentRuleList : actions) {
+        const String& contentRuleListIdentifier = actionsFromContentRuleList.contentRuleListIdentifier;
+        ContentRuleListResults::Result result;
+        for (const auto& action : actionsFromContentRuleList.actions) {
+            switch (action.type()) {
+            case ContentExtensions::ActionType::BlockLoad:
+                results.summary.blockedLoad = true;
+                result.blockedLoad = true;
+                break;
+            case ContentExtensions::ActionType::BlockCookies:
+                results.summary.blockedCookies = true;
+                result.blockedCookies = true;
+                break;
+            case ContentExtensions::ActionType::CSSDisplayNoneSelector:
                 if (resourceType == ResourceType::Document)
-                    initiatingDocumentLoader.addPendingContentExtensionSheet(action.stringArgument(), *styleSheetContents);
+                    initiatingDocumentLoader.addPendingContentExtensionDisplayNoneSelector(contentRuleListIdentifier, action.stringArgument(), action.actionID());
                 else if (currentDocument)
-                    currentDocument->styleSheetCollection().maybeAddContentExtensionSheet(action.stringArgument(), *styleSheetContents);
+                    currentDocument->extensionStyleSheets().addDisplayNoneSelector(contentRuleListIdentifier, action.stringArgument(), action.actionID());
+                break;
+            case ContentExtensions::ActionType::Notify:
+                results.summary.hasNotifications = true;
+                result.notifications.append(action.stringArgument());
+                break;
+            case ContentExtensions::ActionType::MakeHTTPS: {
+                if ((url.protocolIs("http") || url.protocolIs("ws"))
+                    && (!url.port() || WTF::isDefaultPortForProtocol(url.port().value(), url.protocol()))) {
+                    results.summary.madeHTTPS = true;
+                    result.madeHTTPS = true;
+                }
+                break;
+            }
+            case ContentExtensions::ActionType::IgnorePreviousRules:
+                RELEASE_ASSERT_NOT_REACHED();
             }
-            break;
         }
-        case ContentExtensions::ActionType::IgnorePreviousRules:
-        case ContentExtensions::ActionType::InvalidAction:
-            RELEASE_ASSERT_NOT_REACHED();
+
+        if (!actionsFromContentRuleList.sawIgnorePreviousRules) {
+            if (auto* styleSheetContents = globalDisplayNoneStyleSheet(contentRuleListIdentifier)) {
+                if (resourceType == ResourceType::Document)
+                    initiatingDocumentLoader.addPendingContentExtensionSheet(contentRuleListIdentifier, *styleSheetContents);
+                else if (currentDocument)
+                    currentDocument->extensionStyleSheets().maybeAddContentExtensionSheet(contentRuleListIdentifier, *styleSheetContents);
+            }
         }
+
+        results.results.uncheckedAppend({ contentRuleListIdentifier, WTFMove(result) });
     }
 
-    if (willBlockLoad) {
-        if (currentDocument)
-            currentDocument->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Info, makeString("Content blocker prevented frame displaying ", mainDocumentURL.string(), " from loading a resource from ", request.url().string()));
-        request = ResourceRequest();
+    if (currentDocument) {
+        if (results.summary.madeHTTPS) {
+            ASSERT(url.protocolIs("http") || url.protocolIs("ws"));
+            String newProtocol = url.protocolIs("http") ? "https"_s : "wss"_s;
+            currentDocument->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Info, makeString("Content blocker promoted URL from ", url.string(), " to ", newProtocol));
+        }
+        if (results.summary.blockedLoad)
+            currentDocument->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Info, makeString("Content blocker prevented frame displaying ", mainDocumentURL.string(), " from loading a resource from ", url.string()));
     }
+
+    return results;
+}
+
+ContentRuleListResults ContentExtensionsBackend::processContentRuleListsForPingLoad(const URL& url, const URL& mainDocumentURL)
+{
+    if (m_contentExtensions.isEmpty())
+        return { };
+
+    ResourceLoadInfo resourceLoadInfo = { url, mainDocumentURL, ResourceType::Raw };
+    auto actions = actionsForResourceLoad(resourceLoadInfo);
+
+    ContentRuleListResults results;
+    for (const auto& actionsFromContentRuleList : actions) {
+        for (const auto& action : actionsFromContentRuleList.actions) {
+            switch (action.type()) {
+            case ContentExtensions::ActionType::BlockLoad:
+                results.summary.blockedLoad = true;
+                break;
+            case ContentExtensions::ActionType::BlockCookies:
+                results.summary.blockedCookies = true;
+                break;
+            case ContentExtensions::ActionType::MakeHTTPS:
+                if ((url.protocolIs("http") || url.protocolIs("ws")) && (!url.port() || WTF::isDefaultPortForProtocol(url.port().value(), url.protocol())))
+                    results.summary.madeHTTPS = true;
+                break;
+            case ContentExtensions::ActionType::CSSDisplayNoneSelector:
+            case ContentExtensions::ActionType::Notify:
+                // We currently have not implemented notifications from the NetworkProcess to the UIProcess.
+                break;
+            case ContentExtensions::ActionType::IgnorePreviousRules:
+                RELEASE_ASSERT_NOT_REACHED();
+            }
+        }
+    }
+
+    return results;
 }
 
 const String& ContentExtensionsBackend::displayNoneCSSRule()
 {
-    static NeverDestroyed<const String> rule(ASCIILiteral("display:none !important;"));
+    static NeverDestroyed<const String> rule(MAKE_STATIC_STRING_IMPL("display:none !important;"));
     return rule;
 }
 
+void applyResultsToRequest(ContentRuleListResults&& results, Page* page, ResourceRequest& request)
+{
+    if (results.summary.blockedCookies)
+        request.setAllowCookies(false);
+
+    if (results.summary.madeHTTPS) {
+        const URL& originalURL = request.url();
+        ASSERT(originalURL.protocolIs("http"));
+        ASSERT(!originalURL.port() || WTF::isDefaultPortForProtocol(originalURL.port().value(), originalURL.protocol()));
+
+        URL newURL = originalURL;
+        newURL.setProtocol("https");
+        if (originalURL.port())
+            newURL.setPort(WTF::defaultPortForProtocol("https").value());
+        request.setURL(newURL);
+    }
+
+    if (page && results.shouldNotifyApplication()) {
+        results.results.removeAllMatching([](const auto& pair) {
+            return !pair.second.shouldNotifyApplication();
+        });
+        page->chrome().client().contentRuleListNotification(request.url(), results);
+    }
+}
+    
 } // namespace ContentExtensions
 
 } // namespace WebCore