Link prefetch not useful for top-level navigation
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 14 Apr 2019 20:52:46 +0000 (20:52 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 14 Apr 2019 20:52:46 +0000 (20:52 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195623

Patch by Rob Buis <rbuis@igalia.com> on 2019-04-14
Reviewed by Youenn Fablet.

Source/WebCore:

Cache cross-domain top-level prefetches in a dedicated cache and not in the
memory cache. Ignore prefetches for content extension checks.

Tests: http/tests/cache/link-prefetch-main-resource-iframe.html
       http/tests/cache/link-prefetch-main-resource.html

* loader/LinkLoader.cpp:
(WebCore::LinkLoader::prefetchIfNeeded):
* loader/ResourceLoadInfo.cpp:
(WebCore::toResourceType):
* loader/ResourceLoadInfo.h:
* loader/ResourceLoader.cpp:
(WebCore::ResourceLoader::willSendRequestInternal):
* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::requestResource):

Source/WebKit:

Cache cross-domain top-level prefetches in a dedicated cache. When a navigation
to the same url is done within a threshold (5 seconds), reuse the
prefetch cache entry, move it to the disk cache and navigate to
the url, meaning no extra network trip is needed. When not used within
the threshold period, the prefetch entry will be erased using a timer.

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::lowMemoryHandler):
* NetworkProcess/NetworkProcess.h:
(WebKit::NetworkProcess::prefetchCache):
* NetworkProcess/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::retrieveCacheEntry):
(WebKit::NetworkResourceLoader::didReceiveResponse):
(WebKit::NetworkResourceLoader::didReceiveBuffer):
(WebKit::NetworkResourceLoader::tryStoreAsCacheEntry):
(WebKit::NetworkResourceLoader::isCrossOriginPrefetch const):
* NetworkProcess/NetworkResourceLoader.h:
* NetworkProcess/cache/PrefetchCache.cpp: Added.
(WebKit::PrefetchCache::Entry::Entry):
(WebKit::PrefetchCache::PrefetchCache):
(WebKit::PrefetchCache::~PrefetchCache):
(WebKit::PrefetchCache::clear):
(WebKit::PrefetchCache::take):
(WebKit::PrefetchCache::store):
(WebKit::PrefetchCache::sessionPrefetchMap const):
(WebKit::PrefetchCache::clearExpiredEntries):
* NetworkProcess/cache/PrefetchCache.h: Added.
(WebKit::PrefetchCache::Entry::response const):
(WebKit::PrefetchCache::Entry::releaseBuffer):
* Shared/WebPreferences.yaml:
* Sources.txt:
* WebKit.xcodeproj/project.pbxproj:

LayoutTests:

Verify that prefetching a cross-domain top-level main resource
is cached in the prefetch cache and only loaded once, and that non
top-level prefetches keep the old behavior.

* http/tests/cache/link-prefetch-main-resource-expected.txt: Added.
* http/tests/cache/link-prefetch-main-resource-iframe-expected.txt: Added.
* http/tests/cache/link-prefetch-main-resource-iframe.html: Added.
* http/tests/cache/link-prefetch-main-resource.html: Added.
* http/tests/cache/resources/prefetched-main-resource-iframe.php: Added.
* http/tests/cache/resources/prefetched-main-resource.php: Added.
* platform/mac-wk1/TestExpectations:
* platform/win/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@244248 268f45cc-cd09-0410-ab3c-d52691b4dbfc

25 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/cache/link-prefetch-main-resource-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cache/link-prefetch-main-resource-iframe-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cache/link-prefetch-main-resource-iframe.html [new file with mode: 0644]
LayoutTests/http/tests/cache/link-prefetch-main-resource.html [new file with mode: 0644]
LayoutTests/http/tests/cache/resources/prefetched-main-resource-iframe.php [new file with mode: 0644]
LayoutTests/http/tests/cache/resources/prefetched-main-resource.php [new file with mode: 0644]
LayoutTests/platform/mac-wk1/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/loader/LinkLoader.cpp
Source/WebCore/loader/ResourceLoadInfo.cpp
Source/WebCore/loader/ResourceLoadInfo.h
Source/WebCore/loader/ResourceLoader.cpp
Source/WebCore/loader/cache/CachedResourceLoader.cpp
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkProcess.cpp
Source/WebKit/NetworkProcess/NetworkProcess.h
Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp
Source/WebKit/NetworkProcess/NetworkResourceLoader.h
Source/WebKit/NetworkProcess/cache/PrefetchCache.cpp [new file with mode: 0644]
Source/WebKit/NetworkProcess/cache/PrefetchCache.h [new file with mode: 0644]
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/Sources.txt
Source/WebKit/WebKit.xcodeproj/project.pbxproj

index 20faae5..4116c3e 100644 (file)
@@ -1,3 +1,23 @@
+2019-04-14  Rob Buis  <rbuis@igalia.com>
+
+        Link prefetch not useful for top-level navigation
+        https://bugs.webkit.org/show_bug.cgi?id=195623
+
+        Reviewed by Youenn Fablet.
+
+        Verify that prefetching a cross-domain top-level main resource
+        is cached in the prefetch cache and only loaded once, and that non
+        top-level prefetches keep the old behavior.
+
+        * http/tests/cache/link-prefetch-main-resource-expected.txt: Added.
+        * http/tests/cache/link-prefetch-main-resource-iframe-expected.txt: Added.
+        * http/tests/cache/link-prefetch-main-resource-iframe.html: Added.
+        * http/tests/cache/link-prefetch-main-resource.html: Added.
+        * http/tests/cache/resources/prefetched-main-resource-iframe.php: Added.
+        * http/tests/cache/resources/prefetched-main-resource.php: Added.
+        * platform/mac-wk1/TestExpectations:
+        * platform/win/TestExpectations:
+
 2019-04-12  Ross Kirsling  <ross.kirsling@sony.com>
 
         [WinCairo][WKL] Unreviewed test gardening.
diff --git a/LayoutTests/http/tests/cache/link-prefetch-main-resource-expected.txt b/LayoutTests/http/tests/cache/link-prefetch-main-resource-expected.txt
new file mode 100644 (file)
index 0000000..7ef22e9
--- /dev/null
@@ -0,0 +1 @@
+PASS
diff --git a/LayoutTests/http/tests/cache/link-prefetch-main-resource-iframe-expected.txt b/LayoutTests/http/tests/cache/link-prefetch-main-resource-iframe-expected.txt
new file mode 100644 (file)
index 0000000..69cfc5a
--- /dev/null
@@ -0,0 +1,2 @@
+PASS
+
diff --git a/LayoutTests/http/tests/cache/link-prefetch-main-resource-iframe.html b/LayoutTests/http/tests/cache/link-prefetch-main-resource-iframe.html
new file mode 100644 (file)
index 0000000..44366f5
--- /dev/null
@@ -0,0 +1,38 @@
+<html>
+<body>
+<div id="result"></div>
+<script>
+
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+function done()
+{
+    testRunner.notifyDone();
+}
+
+function iframeLoadFinished()
+{
+    if (window.testRunner)
+        setTimeout(done, 0);
+}
+
+function loadAfterPrefetch()
+{
+    var newIframe = document.createElement("iframe");
+    newIframe.src = "http://localhost:8000/cache/resources/prefetched-main-resource-iframe.php";
+    newIframe.onload = iframeLoadFinished;
+    document.body.appendChild(newIframe);
+}
+
+window.onmessage = function(message)
+{
+    document.getElementById('result').textContent = message.data;
+}
+
+</script>
+<link rel="prefetch" href="http://localhost:8000/cache/resources/prefetched-main-resource-iframe.php" onload="setTimeout(loadAfterPrefetch, 0);">
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cache/link-prefetch-main-resource.html b/LayoutTests/http/tests/cache/link-prefetch-main-resource.html
new file mode 100644 (file)
index 0000000..7420df5
--- /dev/null
@@ -0,0 +1,16 @@
+<html>
+<script>
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+function loadAfterPrefetch()
+{
+     window.location.assign('http://localhost:8000/cache/resources/prefetched-main-resource.php');
+}
+</script>
+<body>
+<link rel="prefetch" href="http://localhost:8000/cache/resources/prefetched-main-resource.php" onload="loadAfterPrefetch();">
+</body>
+</html>
diff --git a/LayoutTests/http/tests/cache/resources/prefetched-main-resource-iframe.php b/LayoutTests/http/tests/cache/resources/prefetched-main-resource-iframe.php
new file mode 100644 (file)
index 0000000..a7ad2e9
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+if ($_SERVER["HTTP_PURPOSE"] == "prefetch") {
+    header('Cache-Control: max-age=3600');
+    header("Access-Control-Allow-Origin: http://127.0.0.1:8000");
+
+    echo "<script>";
+    echo "parent.window.postMessage('FAIL', '*');";
+    echo "</script>";
+
+    exit();
+}
+
+header("Access-Control-Allow-Origin: http://127.0.0.1:8000");
+
+echo "<script>";
+echo "parent.window.postMessage('PASS', '*');";
+echo "</script>";
+?>
diff --git a/LayoutTests/http/tests/cache/resources/prefetched-main-resource.php b/LayoutTests/http/tests/cache/resources/prefetched-main-resource.php
new file mode 100644 (file)
index 0000000..7a3ceb4
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+header('Cache-Control: max-age=3600');
+?>
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+
+if (window.testRunner)
+   testRunner.notifyDone();
+
+</script>
+<?php
+if ($_SERVER["HTTP_PURPOSE"] == "prefetch") {
+    print('PASS');
+} else {
+    print('FAIL');
+}
+?>
+</body>
+</html>
index afc96d5..4225da9 100644 (file)
@@ -707,3 +707,6 @@ webkit.org/b/159724 [ Debug ] imported/w3c/web-platform-tests/xhr/send-redirect-
 webkit.org/b/196448 [ Debug ] inspector/audit/basic.html [ Pass Timeout ]
 
 webkit.org/b/196502 media/video-background-tab-playback.html [ Pass Failure ]
+
+webkit.org/b/195623 http/tests/cache/link-prefetch-main-resource.html [ Skip ]
+webkit.org/b/195623 http/tests/cache/link-prefetch-main-resource-iframe.html [ Skip ]
index 94cc469..f0e2a40 100644 (file)
@@ -4330,3 +4330,6 @@ webkit.org/b/196680 printing/page-with-zero-margin.html [ Failure ]
 webkit.org/b/196869 animations/animation-multiple-callbacks-timestamp.html [ Failure ]
 webkit.org/b/196869 legacy-animation-engine/animations/animation-multiple-callbacks-timestamp.html [ Failure ]
 webkit.org/b/196869 imported/mozilla/css-animations/test_animation-currenttime.html [ Failure ]
+
+webkit.org/b/195623 http/tests/cache/link-prefetch-main-resource.html [ Skip ]
+webkit.org/b/195623 http/tests/cache/link-prefetch-main-resource-iframe.html [ Skip ]
index c2d6d5a..ab7a5e3 100644 (file)
@@ -1,3 +1,26 @@
+2019-04-14  Rob Buis  <rbuis@igalia.com>
+
+        Link prefetch not useful for top-level navigation
+        https://bugs.webkit.org/show_bug.cgi?id=195623
+
+        Reviewed by Youenn Fablet.
+
+        Cache cross-domain top-level prefetches in a dedicated cache and not in the
+        memory cache. Ignore prefetches for content extension checks.
+
+        Tests: http/tests/cache/link-prefetch-main-resource-iframe.html
+               http/tests/cache/link-prefetch-main-resource.html
+
+        * loader/LinkLoader.cpp:
+        (WebCore::LinkLoader::prefetchIfNeeded):
+        * loader/ResourceLoadInfo.cpp:
+        (WebCore::toResourceType):
+        * loader/ResourceLoadInfo.h:
+        * loader/ResourceLoader.cpp:
+        (WebCore::ResourceLoader::willSendRequestInternal):
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::CachedResourceLoader::requestResource):
+
 2019-04-14  Dean Jackson  <dino@apple.com>
 
         Extract UTI mapping and allow for additions
index 9f55ae8..33131b1 100644 (file)
@@ -276,8 +276,17 @@ void LinkLoader::prefetchIfNeeded(const LinkRelAttribute& relAttribute, const UR
         m_cachedLinkResource->removeClient(*this);
         m_cachedLinkResource = nullptr;
     }
+    // FIXME: Add further prefetch restrictions/limitations:
+    // - third-party iframes cannot trigger prefetches
+    // - Number of prefetches of a given page is limited (to 1 maybe?)
     ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
     options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck;
+    options.certificateInfoPolicy = CertificateInfoPolicy::IncludeCertificateInfo;
+    options.credentials = FetchOptions::Credentials::SameOrigin;
+    options.redirect = FetchOptions::Redirect::Manual;
+    options.mode = FetchOptions::Mode::Navigate;
+    options.serviceWorkersMode = ServiceWorkersMode::None;
+    options.cachingPolicy = CachingPolicy::DisallowCaching;
     m_cachedLinkResource = document.cachedResourceLoader().requestLinkResource(type, CachedResourceRequest(ResourceRequest(document.completeURL(href)), options, priority)).value_or(nullptr);
     if (m_cachedLinkResource)
         m_cachedLinkResource->addClient(*this);
index 9d92dba..8b74ed5 100644 (file)
@@ -68,8 +68,7 @@ ResourceType toResourceType(CachedResource::Type type)
         return ResourceType::Media;
 #endif
     case CachedResource::Type::LinkPrefetch:
-        ASSERT_NOT_REACHED();
-        break;
+        return ResourceType::Prefetch;
 #if ENABLE(APPLICATION_MANIFEST)
     case CachedResource::Type::ApplicationManifest:
         return ResourceType::Raw;
index 1c123d0..046ba02 100644 (file)
@@ -42,8 +42,9 @@ enum class ResourceType : uint16_t {
     Media = 0x0080,
     PlugInStream = 0x0100,
     Popup = 0x0200,
+    Prefetch = 0x0400,
 };
-const uint16_t ResourceTypeMask = 0x03FF;
+const uint16_t ResourceTypeMask = 0x07FF;
 
 enum class LoadType : uint16_t {
     Invalid = 0x0000,
index 54898ef..ed717cc 100644 (file)
@@ -342,7 +342,7 @@ void ResourceLoader::willSendRequestInternal(ResourceRequest&& request, const Re
 
     ASSERT(!m_reachedTerminalState);
 #if ENABLE(CONTENT_EXTENSIONS)
-    ASSERT(m_resourceType != ResourceType::Invalid);
+    ASSERT(m_resourceType != ResourceType::Invalid || m_resourceType == ResourceType::Prefetch);
 #endif
 
     // We need a resource identifier for all requests, even if FrameLoader is never going to see it (such as with CORS preflight requests).
@@ -353,7 +353,7 @@ void ResourceLoader::willSendRequestInternal(ResourceRequest&& request, const Re
     }
 
 #if ENABLE(CONTENT_EXTENSIONS)
-    if (!redirectResponse.isNull() && frameLoader()) {
+    if (!redirectResponse.isNull() && frameLoader() && m_resourceType != ResourceType::Prefetch) {
         Page* page = frameLoader()->frame().page();
         if (page && m_documentLoader) {
             auto results = page->userContentProvider().processContentRuleListsForLoad(request.url(), m_resourceType, *m_documentLoader);
index b65cf17..9298a91 100644 (file)
@@ -806,7 +806,7 @@ ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requ
     }
 
 #if ENABLE(CONTENT_EXTENSIONS)
-    if (frame() && frame()->page() && m_documentLoader) {
+    if (frame() && frame()->page() && m_documentLoader && type != CachedResource::Type::LinkPrefetch) {
         const auto& resourceRequest = request.resourceRequest();
         auto* page = frame()->page();
         auto results = page->userContentProvider().processContentRuleListsForLoad(resourceRequest.url(), toResourceType(type), *m_documentLoader);
index ba936f2..eee0ec6 100644 (file)
@@ -1,3 +1,43 @@
+2019-04-14  Rob Buis  <rbuis@igalia.com>
+
+        Link prefetch not useful for top-level navigation
+        https://bugs.webkit.org/show_bug.cgi?id=195623
+
+        Reviewed by Youenn Fablet.
+
+        Cache cross-domain top-level prefetches in a dedicated cache. When a navigation
+        to the same url is done within a threshold (5 seconds), reuse the
+        prefetch cache entry, move it to the disk cache and navigate to
+        the url, meaning no extra network trip is needed. When not used within
+        the threshold period, the prefetch entry will be erased using a timer.
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::lowMemoryHandler):
+        * NetworkProcess/NetworkProcess.h:
+        (WebKit::NetworkProcess::prefetchCache):
+        * NetworkProcess/NetworkResourceLoader.cpp:
+        (WebKit::NetworkResourceLoader::retrieveCacheEntry):
+        (WebKit::NetworkResourceLoader::didReceiveResponse):
+        (WebKit::NetworkResourceLoader::didReceiveBuffer):
+        (WebKit::NetworkResourceLoader::tryStoreAsCacheEntry):
+        (WebKit::NetworkResourceLoader::isCrossOriginPrefetch const):
+        * NetworkProcess/NetworkResourceLoader.h:
+        * NetworkProcess/cache/PrefetchCache.cpp: Added.
+        (WebKit::PrefetchCache::Entry::Entry):
+        (WebKit::PrefetchCache::PrefetchCache):
+        (WebKit::PrefetchCache::~PrefetchCache):
+        (WebKit::PrefetchCache::clear):
+        (WebKit::PrefetchCache::take):
+        (WebKit::PrefetchCache::store):
+        (WebKit::PrefetchCache::sessionPrefetchMap const):
+        (WebKit::PrefetchCache::clearExpiredEntries):
+        * NetworkProcess/cache/PrefetchCache.h: Added.
+        (WebKit::PrefetchCache::Entry::response const):
+        (WebKit::PrefetchCache::Entry::releaseBuffer):
+        * Shared/WebPreferences.yaml:
+        * Sources.txt:
+        * WebKit.xcodeproj/project.pbxproj:
+
 2019-04-14  Andy Estes  <aestes@apple.com>
 
         [Cocoa] WKCustomProtocolLoader should store a WeakPtr to its LegacyCustomProtocolManagerProxy
index 076bc82..ebcc9be 100644 (file)
@@ -270,6 +270,8 @@ void NetworkProcess::lowMemoryHandler(Critical critical)
         return;
 
     WTF::releaseFastMallocFreeMemory();
+
+    m_prefetchCache.clear();
 }
 
 void NetworkProcess::initializeNetworkProcess(NetworkProcessCreationParameters&& parameters)
index 7ae4946..0bfe3dc 100644 (file)
@@ -31,6 +31,7 @@
 #include "NetworkBlobRegistry.h"
 #include "NetworkContentRuleListManager.h"
 #include "NetworkHTTPSUpgradeChecker.h"
+#include "PrefetchCache.h"
 #include "SandboxExtension.h"
 #include "WebResourceLoadStatisticsStore.h"
 #include "WebsiteData.h"
@@ -330,6 +331,8 @@ public:
 
     WebCore::StorageQuotaManager& storageQuotaManager(PAL::SessionID, const WebCore::ClientOrigin&);
 
+    PrefetchCache& prefetchCache() { return m_prefetchCache; }
+
 private:
     void platformInitializeNetworkProcess(const NetworkProcessCreationParameters&);
     std::unique_ptr<WebCore::NetworkStorageSession> platformCreateDefaultStorageSession() const;
@@ -551,6 +554,8 @@ private:
     };
     HashMap<PAL::SessionID, StorageQuotaManagers> m_storageQuotaManagers;
     uint32_t m_downloadMonitorSpeedMultiplier { 1 };
+
+    PrefetchCache m_prefetchCache;
 };
 
 } // namespace WebKit
index f1459d7..a591b9d 100644 (file)
@@ -212,6 +212,10 @@ void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
     ASSERT(canUseCache(request));
 
     RefPtr<NetworkResourceLoader> loader(this);
+    if (isMainFrameLoad() && m_parameters.options.mode == FetchOptions::Mode::Navigate) {
+        if (auto entry = m_connection->networkProcess().prefetchCache().take(sessionID(), request.url()))
+            m_cache->store(request, entry->response, entry->releaseBuffer(), nullptr);
+    }
     m_cache->retrieve(request, { m_parameters.webPageID, m_parameters.webFrameID }, [this, loader = WTFMove(loader), request = ResourceRequest { request }](auto entry, auto info) mutable {
         if (loader->hasOneRef()) {
             // The loader has been aborted and is only held alive by this lambda.
@@ -461,6 +465,10 @@ void NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedRespon
         return completionHandler(PolicyAction::Use);
     }
 
+    if (isCrossOriginPrefetch(originalRequest())) {
+        send(Messages::WebResourceLoader::DidReceiveResponse { ResourceResponse { }, false });
+        return completionHandler(PolicyAction::Use);
+    }
     // We wait to receive message NetworkResourceLoader::ContinueDidReceiveResponse before continuing a load for
     // a main resource because the embedding client must decide whether to allow the load.
     bool willWaitForContinueDidReceiveResponse = isMainResource();
@@ -487,6 +495,8 @@ void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int rep
         else
             m_bufferedDataForCache = nullptr;
     }
+    if (isCrossOriginPrefetch(originalRequest()))
+        return;
     // FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
     unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
 
@@ -769,6 +779,10 @@ void NetworkResourceLoader::tryStoreAsCacheEntry()
     if (!m_bufferedDataForCache)
         return;
 
+    if (isCrossOriginPrefetch(originalRequest())) {
+        m_connection->networkProcess().prefetchCache().store(sessionID(), m_networkLoad->currentRequest().url(), WTFMove(m_response), WTFMove(m_bufferedDataForCache));
+        return;
+    }
     m_cache->store(m_networkLoad->currentRequest(), m_response, WTFMove(m_bufferedDataForCache), [loader = makeRef(*this)](auto& mappedBody) mutable {
 #if ENABLE(SHAREABLE_RESOURCE)
         if (mappedBody.shareableResourceHandle.isNull())
@@ -1099,6 +1113,11 @@ void NetworkResourceLoader::logSlowCacheRetrieveIfNeeded(const NetworkCache::Cac
 #endif
 }
 
+bool NetworkResourceLoader::isCrossOriginPrefetch(const ResourceRequest& request) const
+{
+    return request.httpHeaderField(HTTPHeaderName::Purpose) == "prefetch" && !m_parameters.sourceOrigin->canRequest(request.url());
+}
+
 } // namespace WebKit
 
 #undef RELEASE_LOG_IF_ALLOWED
index 5bee9e2..69b480a 100644 (file)
@@ -103,6 +103,7 @@ public:
 
     bool isMainResource() const { return m_parameters.request.requester() == WebCore::ResourceRequest::Requester::Main; }
     bool isMainFrameLoad() const { return isMainResource() && m_parameters.frameAncestorOrigins.isEmpty(); }
+    bool isCrossOriginPrefetch(const WebCore::ResourceRequest&) const;
 
     bool isAlwaysOnLoggingAllowed() const;
 
diff --git a/Source/WebKit/NetworkProcess/cache/PrefetchCache.cpp b/Source/WebKit/NetworkProcess/cache/PrefetchCache.cpp
new file mode 100644 (file)
index 0000000..e79820d
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PrefetchCache.h"
+
+#include <WebCore/HTTPHeaderNames.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace WebKit {
+
+PrefetchCache::Entry::Entry(WebCore::ResourceResponse&& response, RefPtr<WebCore::SharedBuffer>&& buffer)
+    : response(WTFMove(response)), buffer(WTFMove(buffer))
+{
+}
+
+PrefetchCache::PrefetchCache()
+    : m_expirationTimer(*this, &PrefetchCache::clearExpiredEntries)
+{
+}
+
+PrefetchCache::~PrefetchCache()
+{
+}
+
+void PrefetchCache::clear()
+{
+    m_expirationTimer.stop();
+    m_sessionExpirationList.clear();
+    m_sessionPrefetches.clear();
+}
+
+std::unique_ptr<PrefetchCache::Entry> PrefetchCache::take(PAL::SessionID sessionID, const URL& url)
+{
+    auto* resources = sessionPrefetchMap(sessionID);
+    if (!resources)
+        return nullptr;
+    return resources->take(url);
+}
+
+static const Seconds expirationTimeout { 5_s };
+
+void PrefetchCache::store(PAL::SessionID sessionID, const URL& requestUrl, WebCore::ResourceResponse&& response, RefPtr<WebCore::SharedBuffer>&& buffer)
+{
+    ASSERT(sessionID.isValid());
+    m_sessionPrefetches.ensure(sessionID, [&] {
+        return std::make_unique<PrefetchEntriesMap>();
+    }).iterator->value->set(requestUrl, std::make_unique<PrefetchCache::Entry>(WTFMove(response), WTFMove(buffer)));
+    m_sessionExpirationList.append(std::make_tuple(sessionID, requestUrl, WallTime::now()));
+    if (!m_expirationTimer.isActive())
+        m_expirationTimer.startOneShot(expirationTimeout);
+}
+
+auto PrefetchCache::sessionPrefetchMap(PAL::SessionID sessionID) const -> PrefetchEntriesMap*
+{
+    ASSERT(sessionID.isValid());
+    return m_sessionPrefetches.get(sessionID);
+}
+
+void PrefetchCache::clearExpiredEntries()
+{
+    PAL::SessionID sessionID;
+    URL requestUrl;
+    WallTime timestamp;
+    auto timeout = WallTime::now();
+    while (!m_sessionExpirationList.isEmpty()) {
+        std::tie(sessionID, requestUrl, timestamp) = m_sessionExpirationList.first();
+        auto* resources = sessionPrefetchMap(sessionID);
+        ASSERT(resources);
+        ASSERT(resources->contains(requestUrl));
+        auto elapsed = timeout - timestamp;
+        if (elapsed > expirationTimeout) {
+            resources->remove(requestUrl);
+            m_sessionExpirationList.removeFirst();
+        } else {
+            m_expirationTimer.startOneShot(expirationTimeout - elapsed);
+            break;
+        }
+    }
+}
+
+}
diff --git a/Source/WebKit/NetworkProcess/cache/PrefetchCache.h b/Source/WebKit/NetworkProcess/cache/PrefetchCache.h
new file mode 100644 (file)
index 0000000..e05463d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <WebCore/ResourceResponse.h>
+#include <WebCore/SharedBuffer.h>
+#include <WebCore/Timer.h>
+#include <wtf/Deque.h>
+#include <wtf/HashMap.h>
+#include <wtf/URLHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebKit {
+
+class PrefetchCache {
+    WTF_MAKE_NONCOPYABLE(PrefetchCache);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    PrefetchCache();
+    ~PrefetchCache();
+
+    void clear();
+
+    struct Entry {
+        Entry(WebCore::ResourceResponse&&, RefPtr<WebCore::SharedBuffer>&&);
+
+        RefPtr<WebCore::SharedBuffer> releaseBuffer() { return buffer.releaseNonNull(); }
+
+        WebCore::ResourceResponse response;
+        RefPtr<WebCore::SharedBuffer> buffer;
+    };
+
+    std::unique_ptr<Entry> take(PAL::SessionID, const URL&);
+    void store(PAL::SessionID, const URL&, WebCore::ResourceResponse&&, RefPtr<WebCore::SharedBuffer>&&);
+
+private:
+    void clearExpiredEntries();
+
+    using PrefetchEntriesMap = HashMap<URL, std::unique_ptr<Entry>>;
+    PrefetchEntriesMap* sessionPrefetchMap(PAL::SessionID) const;
+
+    using SessionPrefetchResourceMap = HashMap<PAL::SessionID, std::unique_ptr<PrefetchEntriesMap>>;
+    SessionPrefetchResourceMap m_sessionPrefetches;
+
+    using SessionPrefetchExpirationList = Deque<std::tuple<PAL::SessionID, URL, WallTime>>;
+    SessionPrefetchExpirationList m_sessionExpirationList;
+
+    WebCore::Timer m_expirationTimer;
+};
+
+} // namespace WebKit
index e9ee671..f3fe546 100644 (file)
@@ -1644,6 +1644,14 @@ ApplePayRemoteUIEnabled:
   humanReadableName: "Apple Pay Remote UI"
   type: bool
 
+LinkPrefetchEnabled:
+  type: bool
+  defaultValue: false
+  humanReadableName: "LinkPrefetch"
+  humanReadableDescription: "Enable LinkedPrefetch"
+  webcoreBinding: RuntimeEnabledFeatures
+  category: experimental
+
 # Deprecated
 
 ICECandidateFilteringEnabled:
index 803b576..881d14a 100644 (file)
@@ -80,6 +80,7 @@ NetworkProcess/cache/NetworkCacheSpeculativeLoad.cpp
 NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp
 NetworkProcess/cache/NetworkCacheStorage.cpp
 NetworkProcess/cache/NetworkCacheSubresourcesEntry.cpp
+NetworkProcess/cache/PrefetchCache.cpp
 
 NetworkProcess/webrtc/NetworkMDNSRegister.cpp
 
index acc824e..4c94689 100644 (file)
                A78CCDDB193AC9F8005ECC25 /* com.apple.WebKit.Networking.sb in CopyFiles */ = {isa = PBXBuildFile; fileRef = A78CCDD8193AC9E3005ECC25 /* com.apple.WebKit.Networking.sb */; };
                A78CCDDC193AC9FB005ECC25 /* com.apple.WebKit.WebContent.sb in CopyFiles */ = {isa = PBXBuildFile; fileRef = A78CCDD9193AC9E3005ECC25 /* com.apple.WebKit.WebContent.sb */; };
                A7D792D81767CCA300881CBE /* ActivityAssertion.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D792D41767CB0900881CBE /* ActivityAssertion.h */; };
+               AAB145E6223F931200E489D8 /* PrefetchCache.h in Headers */ = {isa = PBXBuildFile; fileRef = AAB145E4223F931200E489D8 /* PrefetchCache.h */; };
                B62E7312143047B00069EC35 /* WKHitTestResult.h in Headers */ = {isa = PBXBuildFile; fileRef = B62E7311143047B00069EC35 /* WKHitTestResult.h */; settings = {ATTRIBUTES = (Private, ); }; };
                B878B615133428DC006888E9 /* CorrectionPanel.h in Headers */ = {isa = PBXBuildFile; fileRef = B878B613133428DC006888E9 /* CorrectionPanel.h */; };
                BC017D0716260FF4007054F5 /* WKDOMDocument.h in Headers */ = {isa = PBXBuildFile; fileRef = BC017CFF16260FF4007054F5 /* WKDOMDocument.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A7D792D41767CB0900881CBE /* ActivityAssertion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ActivityAssertion.h; sourceTree = "<group>"; };
                A7D792D51767CB6E00881CBE /* ActivityAssertion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ActivityAssertion.cpp; sourceTree = "<group>"; };
                A7E93CEB192531AA00A1DC48 /* AuxiliaryProcessIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AuxiliaryProcessIOS.mm; path = ios/AuxiliaryProcessIOS.mm; sourceTree = "<group>"; };
+               AAB145E4223F931200E489D8 /* PrefetchCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrefetchCache.h; sourceTree = "<group>"; };
+               AAB145E5223F931200E489D8 /* PrefetchCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PrefetchCache.cpp; sourceTree = "<group>"; };
                B396EA5512E0ED2D00F4FEB7 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
                B62E730F143047A60069EC35 /* WKHitTestResult.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WKHitTestResult.cpp; sourceTree = "<group>"; };
                B62E7311143047B00069EC35 /* WKHitTestResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKHitTestResult.h; sourceTree = "<group>"; };
                                E4436EC21A0CFDB200EAD204 /* NetworkCacheStorage.h */,
                                8310428A1BD6B66F00A715E4 /* NetworkCacheSubresourcesEntry.cpp */,
                                831042891BD6B66F00A715E4 /* NetworkCacheSubresourcesEntry.h */,
+                               AAB145E5223F931200E489D8 /* PrefetchCache.cpp */,
+                               AAB145E4223F931200E489D8 /* PrefetchCache.h */,
                        );
                        path = cache;
                        sourceTree = "<group>";
                                7CD622781739D863005BD7FF /* PluginSandboxProfile.h in Headers */,
                                1A6FB7AF11E64B6800DB1371 /* PluginView.h in Headers */,
                                83A0ED351F747CCF003299EB /* PreconnectTask.h in Headers */,
+                               AAB145E6223F931200E489D8 /* PrefetchCache.h in Headers */,
                                E1CC1B9012D7EADF00625838 /* PrintInfo.h in Headers */,
                                86F9536518FF58F5001DB2EF /* ProcessAssertion.h in Headers */,
                                BC1A7C581136E19C00FB7167 /* ProcessLauncher.h in Headers */,