Link prefetch not useful for top-level navigation
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 May 2019 12:40:10 +0000 (12:40 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 May 2019 12:40:10 +0000 (12:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195623

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

Source/WebCore:

Cache cross-domain top-level prefetches in a dedicated cache and not in the
memory cache.

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

* loader/LinkLoader.cpp:
(WebCore::LinkLoader::prefetchIfNeeded):
* loader/ResourceLoadInfo.cpp:
(WebCore::toResourceType):

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/NetworkResourceLoader.cpp:
(WebKit::NetworkResourceLoader::retrieveCacheEntry):
(WebKit::NetworkResourceLoader::didReceiveResponse):
(WebKit::NetworkResourceLoader::didReceiveBuffer):
(WebKit::NetworkResourceLoader::tryStoreAsCacheEntry):
(WebKit::NetworkResourceLoader::isCrossOriginPrefetch const):
* NetworkProcess/NetworkResourceLoader.h:
* NetworkProcess/NetworkSession.h:
(WebKit::NetworkSession::prefetchCache):
(WebKit::NetworkSession::clearPrefetchCache):
* 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::clearExpiredEntries):
* NetworkProcess/cache/PrefetchCache.h: Added.
(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.
* http/tests/contentextensions/prefetch-blocked-expected.txt: Added.
* http/tests/contentextensions/prefetch-blocked.html: Added.
* http/tests/contentextensions/prefetch-blocked.html.json: Added.
* platform/mac-wk1/TestExpectations:
* platform/win/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245053 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/http/tests/contentextensions/prefetch-blocked-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/contentextensions/prefetch-blocked.html [new file with mode: 0644]
LayoutTests/http/tests/contentextensions/prefetch-blocked.html.json [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/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkProcess.cpp
Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp
Source/WebKit/NetworkProcess/NetworkResourceLoader.h
Source/WebKit/NetworkProcess/NetworkSession.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 737451a..2f702f4 100644 (file)
@@ -1,3 +1,26 @@
+2019-05-08  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.
+        * http/tests/contentextensions/prefetch-blocked-expected.txt: Added.
+        * http/tests/contentextensions/prefetch-blocked.html: Added.
+        * http/tests/contentextensions/prefetch-blocked.html.json: Added.
+        * platform/mac-wk1/TestExpectations:
+        * platform/win/TestExpectations:
+
 2019-05-08  Jiewen Tan  <jiewen_tan@apple.com>
 
         Unreviewed, a build fix after r245043
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>
diff --git a/LayoutTests/http/tests/contentextensions/prefetch-blocked-expected.txt b/LayoutTests/http/tests/contentextensions/prefetch-blocked-expected.txt
new file mode 100644 (file)
index 0000000..362195b
--- /dev/null
@@ -0,0 +1,4 @@
+CONSOLE MESSAGE: line 8: Content blocker prevented frame displaying http://127.0.0.1:8000/contentextensions/resources/should-not-load.html from loading a resource from http://127.0.0.1:8000/contentextensions/resources/should-not-load.html
+This page prefetches a Document.
+The prefetch should be blocked by the content extension filter
+
diff --git a/LayoutTests/http/tests/contentextensions/prefetch-blocked.html b/LayoutTests/http/tests/contentextensions/prefetch-blocked.html
new file mode 100644 (file)
index 0000000..d7d464a
--- /dev/null
@@ -0,0 +1,8 @@
+This page prefetches a Document.<br>
+The prefetch should be blocked by the content extension filter<br>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+}
+</script>
+<link rel="prefetch" href="resources/should-not-load.html">
diff --git a/LayoutTests/http/tests/contentextensions/prefetch-blocked.html.json b/LayoutTests/http/tests/contentextensions/prefetch-blocked.html.json
new file mode 100644 (file)
index 0000000..4785329
--- /dev/null
@@ -0,0 +1,11 @@
+[
+    {
+        "action": {
+            "type": "block"
+        },
+        "trigger": {
+            "url-filter": "should-not-load",
+            "resource-type": ["document"]
+        }
+    }
+]
index 956eede..5365880 100644 (file)
@@ -702,3 +702,6 @@ 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/196915 [ Debug ] inspector/timeline/timeline-recording.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 c5d8e65..9d9773f 100644 (file)
@@ -4395,3 +4395,6 @@ webkit.org/b/197310 fast/harness/render-tree-as-text-options.html [ Failure ]
 [ Win10 ] svg/repaint/change-background-color.html [ ImageOnlyFailure ]
 [ Win10 ] svg/repaint/remove-background-property-on-root.html [ ImageOnlyFailure ]
 [ Win10 ] fast/spatial-navigation/snav-media-elements.html [ Crash ]
+
+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 4f5c29d..6210b94 100644 (file)
@@ -1,3 +1,22 @@
+2019-05-08  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.
+
+        Tests: http/tests/cache/link-prefetch-main-resource-iframe.html
+               http/tests/cache/link-prefetch-main-resource.html
+               http/tests/contentextensions/prefetch-blocked.html
+
+        * loader/LinkLoader.cpp:
+        (WebCore::LinkLoader::prefetchIfNeeded):
+        * loader/ResourceLoadInfo.cpp:
+        (WebCore::toResourceType):
+
 2019-05-07  Don Olmstead  <don.olmstead@sony.com>
 
         Fix !HAVE(ACCESSIBILITY) build
index 91861e1..2cd76f9 100644 (file)
@@ -277,8 +277,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 f2c3f21..7f7f682 100644 (file)
@@ -37,6 +37,7 @@ namespace ContentExtensions {
 ResourceType toResourceType(CachedResource::Type type)
 {
     switch (type) {
+    case CachedResource::Type::LinkPrefetch:
     case CachedResource::Type::MainResource:
         return ResourceType::Document;
     case CachedResource::Type::SVGDocumentResource:
@@ -71,9 +72,6 @@ ResourceType toResourceType(CachedResource::Type type)
     case CachedResource::Type::TextTrackResource:
         return ResourceType::Media;
 #endif
-    case CachedResource::Type::LinkPrefetch:
-        ASSERT_NOT_REACHED();
-        break;
 #if ENABLE(APPLICATION_MANIFEST)
     case CachedResource::Type::ApplicationManifest:
         return ResourceType::Raw;
index 35bd7df..b6d3be8 100644 (file)
@@ -1,3 +1,42 @@
+2019-05-08  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/NetworkResourceLoader.cpp:
+        (WebKit::NetworkResourceLoader::retrieveCacheEntry):
+        (WebKit::NetworkResourceLoader::didReceiveResponse):
+        (WebKit::NetworkResourceLoader::didReceiveBuffer):
+        (WebKit::NetworkResourceLoader::tryStoreAsCacheEntry):
+        (WebKit::NetworkResourceLoader::isCrossOriginPrefetch const):
+        * NetworkProcess/NetworkResourceLoader.h:
+        * NetworkProcess/NetworkSession.h:
+        (WebKit::NetworkSession::prefetchCache):
+        (WebKit::NetworkSession::clearPrefetchCache):
+        * 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::clearExpiredEntries):
+        * NetworkProcess/cache/PrefetchCache.h: Added.
+        (WebKit::PrefetchCache::Entry::releaseBuffer):
+        * Shared/WebPreferences.yaml:
+        * Sources.txt:
+        * WebKit.xcodeproj/project.pbxproj:
+
 2019-05-07  Chris Dumez  <cdumez@apple.com>
 
         Simplify logic to prevent App Nap in WebPage
index 0e7180d..a3b3b35 100644 (file)
@@ -270,6 +270,9 @@ void NetworkProcess::lowMemoryHandler(Critical critical)
         return;
 
     WTF::releaseFastMallocFreeMemory();
+
+    for (auto& networkSession : m_networkSessions.values())
+        networkSession.get().clearPrefetchCache();
 }
 
 void NetworkProcess::initializeNetworkProcess(NetworkProcessCreationParameters&& parameters)
index 8ab0466..4510e92 100644 (file)
@@ -211,6 +211,13 @@ void NetworkResourceLoader::retrieveCacheEntry(const ResourceRequest& request)
     ASSERT(canUseCache(request));
 
     RefPtr<NetworkResourceLoader> loader(this);
+    if (isMainFrameLoad()) {
+        ASSERT(m_parameters.options.mode == FetchOptions::Mode::Navigate);
+        if (auto session = m_connection->networkProcess().networkSession(sessionID())) {
+            if (auto entry = session->prefetchCache().take(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.
@@ -469,6 +476,9 @@ void NetworkResourceLoader::didReceiveResponse(ResourceResponse&& receivedRespon
         return completionHandler(PolicyAction::Use);
     }
 
+    if (isCrossOriginPrefetch())
+        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();
@@ -506,6 +516,8 @@ void NetworkResourceLoader::didReceiveBuffer(Ref<SharedBuffer>&& buffer, int rep
         else
             m_bufferedDataForCache = nullptr;
     }
+    if (isCrossOriginPrefetch())
+        return;
     // FIXME: At least on OS X Yosemite we always get -1 from the resource handle.
     unsigned encodedDataLength = reportedEncodedDataLength >= 0 ? reportedEncodedDataLength : buffer->size();
 
@@ -797,6 +809,11 @@ void NetworkResourceLoader::tryStoreAsCacheEntry()
     if (!m_bufferedDataForCache)
         return;
 
+    if (isCrossOriginPrefetch()) {
+        if (auto session = m_connection->networkProcess().networkSession(sessionID()))
+            session->prefetchCache().store(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())
@@ -1127,6 +1144,12 @@ void NetworkResourceLoader::logSlowCacheRetrieveIfNeeded(const NetworkCache::Cac
 #endif
 }
 
+bool NetworkResourceLoader::isCrossOriginPrefetch() const
+{
+    auto request = originalRequest();
+    return request.httpHeaderField(HTTPHeaderName::Purpose) == "prefetch" && !m_parameters.sourceOrigin->canRequest(request.url());
+}
+
 } // namespace WebKit
 
 #undef RELEASE_LOG_IF_ALLOWED
index c7de3c0..a8a3005 100644 (file)
@@ -104,6 +104,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;
 
     bool isAlwaysOnLoggingAllowed() const;
 
index 8140ad9..2205a99 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "PrefetchCache.h"
 #include "WebResourceLoadStatisticsStore.h"
 #include <WebCore/AdClickAttribution.h>
 #include <WebCore/RegistrableDomain.h>
@@ -93,6 +94,9 @@ public:
     void addKeptAliveLoad(Ref<NetworkResourceLoader>&&);
     void removeKeptAliveLoad(NetworkResourceLoader&);
 
+    PrefetchCache& prefetchCache() { return m_prefetchCache; }
+    void clearPrefetchCache() { m_prefetchCache.clear(); }
+
 protected:
     NetworkSession(NetworkProcess&, PAL::SessionID);
 
@@ -109,6 +113,8 @@ protected:
     UniqueRef<AdClickAttributionManager> m_adClickAttribution;
 
     HashSet<Ref<NetworkResourceLoader>> m_keptAliveLoads;
+
+    PrefetchCache m_prefetchCache;
 };
 
 } // namespace WebKit
diff --git a/Source/WebKit/NetworkProcess/cache/PrefetchCache.cpp b/Source/WebKit/NetworkProcess/cache/PrefetchCache.cpp
new file mode 100644 (file)
index 0000000..73cab47
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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>
+
+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();
+    if (m_sessionPrefetches)
+        m_sessionPrefetches->clear();
+}
+
+std::unique_ptr<PrefetchCache::Entry> PrefetchCache::take(const URL& url)
+{
+    auto* resources = m_sessionPrefetches.get();
+    if (!resources)
+        return nullptr;
+    m_sessionExpirationList.removeAllMatching([&url] (const auto& tuple) {
+        return std::get<0>(tuple) == url;
+    });
+    return resources->take(url);
+}
+
+static const Seconds expirationTimeout { 5_s };
+
+void PrefetchCache::store(const URL& requestUrl, WebCore::ResourceResponse&& response, RefPtr<WebCore::SharedBuffer>&& buffer)
+{
+    if (!m_sessionPrefetches)
+        m_sessionPrefetches = std::make_unique<PrefetchEntriesMap>();
+    m_sessionPrefetches->set(requestUrl, std::make_unique<PrefetchCache::Entry>(WTFMove(response), WTFMove(buffer)));
+    m_sessionExpirationList.append(std::make_tuple(requestUrl, WallTime::now()));
+    if (!m_expirationTimer.isActive())
+        m_expirationTimer.startOneShot(expirationTimeout);
+}
+
+void PrefetchCache::clearExpiredEntries()
+{
+    URL requestUrl;
+    WallTime timestamp;
+    auto timeout = WallTime::now();
+    while (!m_sessionExpirationList.isEmpty()) {
+        std::tie(requestUrl, timestamp) = m_sessionExpirationList.first();
+        auto* resources = m_sessionPrefetches.get();
+        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..c8895fe
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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>&&);
+
+        Ref<WebCore::SharedBuffer> releaseBuffer() { return buffer.releaseNonNull(); }
+
+        WebCore::ResourceResponse response;
+        RefPtr<WebCore::SharedBuffer> buffer;
+    };
+
+    std::unique_ptr<Entry> take(const URL&);
+    void store(const URL&, WebCore::ResourceResponse&&, RefPtr<WebCore::SharedBuffer>&&);
+
+private:
+    void clearExpiredEntries();
+
+    using PrefetchEntriesMap = HashMap<URL, std::unique_ptr<Entry>>;
+    std::unique_ptr<PrefetchEntriesMap> m_sessionPrefetches;
+
+    using SessionPrefetchExpirationList = Deque<std::tuple<URL, WallTime>>;
+    SessionPrefetchExpirationList m_sessionExpirationList;
+
+    WebCore::Timer m_expirationTimer;
+};
+
+} // namespace WebKit
index 17936f4..8d24ac7 100644 (file)
@@ -1665,6 +1665,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 799c6f0..48f58e3 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 32c6820..a867424 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 */,