A service worker process should app nap when all its clients app nap
authoryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 May 2019 19:48:51 +0000 (19:48 +0000)
committeryouenn@apple.com <youenn@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 May 2019 19:48:51 +0000 (19:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=185626
<rdar://problem/46785908>

Reviewed by Alex Christensen.

Source/WebCore:

Update RegistrableDomain to work with SecurityOriginData.
Add internal API to enable accessing to service worker process throttle state.

Test: http/wpt/service-workers/mac/processSuppression.https.html

* platform/RegistrableDomain.h:
(WebCore::RegistrableDomain::RegistrableDomain):
(WebCore::RegistrableDomain::matches const):
(WebCore::RegistrableDomain::registrableDomainFromHost):
* testing/ServiceWorkerInternals.cpp:
(WebCore::ServiceWorkerInternals::isThrottleable const):
* testing/ServiceWorkerInternals.h:
* testing/ServiceWorkerInternals.idl:
* workers/service/SWClientConnection.h:
* workers/service/context/SWContextManager.cpp:
* workers/service/context/SWContextManager.h:
* workers/service/server/SWServer.cpp:
(WebCore::SWServer::serverToContextConnectionCreated):
* workers/service/server/SWServer.h:
(WebCore::SWServer::Connection::server const):
(WebCore::SWServer::connections const):
* workers/service/server/SWServerToContextConnection.h:

Source/WebKit:

Compute whether a given web process can be throttled on every page throttling change.
Send that information to network process which stores that information in WebSWServerConnection.
Every WebSWServerToContextConnection throttle state is then computed based on all WebSWServerConnection
that have a client that matches the registrable domain of the context connection.

* NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
(WebKit::WebSWServerConnection::registerServiceWorkerClient):
(WebKit::WebSWServerConnection::unregisterServiceWorkerClient):
(WebKit::WebSWServerConnection::hasMatchingClient const):
(WebKit::WebSWServerConnection::computeThrottleState const):
(WebKit::WebSWServerConnection::setThrottleState):
(WebKit::WebSWServerConnection::updateThrottleState):
(WebKit::WebSWServerConnection::serverToContextConnectionCreated):
* NetworkProcess/ServiceWorker/WebSWServerConnection.h:
(WebKit::WebSWServerConnection::isThrottleable const):
* NetworkProcess/ServiceWorker/WebSWServerConnection.messages.in:
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
(WebKit::WebSWServerToContextConnection::setThrottleState):
* NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
(WebKit::WebSWServerToContextConnection::isThrottleable const):
* UIProcess/ServiceWorkerProcessProxy.cpp:
* UIProcess/ServiceWorkerProcessProxy.h:
* WebProcess/Storage/WebSWClientConnection.cpp:
(WebKit::WebSWClientConnection::WebSWClientConnection):
(WebKit::WebSWClientConnection::updateThrottleState):
* WebProcess/Storage/WebSWClientConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::setThrottleState):
(WebKit::WebSWContextManagerConnection::isThrottleable const):
* WebProcess/Storage/WebSWContextManagerConnection.h:
* WebProcess/Storage/WebSWContextManagerConnection.messages.in:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::updateUserActivity):
(WebKit::WebPage::isThrottleable const):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebProcess.cpp:
(WebKit::WebProcess::arePagesThrottleable const):
* WebProcess/WebProcess.h:

Tools:

Allow to enable app nap through test header.

* WebKitTestRunner/TestController.cpp:
(WTR::TestController::resetPreferencesToConsistentValues):
(WTR::updateTestOptionsFromTestHeader):
* WebKitTestRunner/TestOptions.h:
(WTR::TestOptions::hasSameInitializationOptions const):

LayoutTests:

* http/wpt/service-workers/mac/throttleable-worker.js: Added.
* http/wpt/service-workers/mac/throttleable.https-expected.txt: Added.
* http/wpt/service-workers/mac/throttleable.https.html: Added.
* platform/ios-wk2/TestExpectations:

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

35 files changed:
LayoutTests/ChangeLog
LayoutTests/http/wpt/service-workers/mac/throttleable-worker.js [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/mac/throttleable.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/service-workers/mac/throttleable.https.html [new file with mode: 0644]
LayoutTests/platform/ios-wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/platform/RegistrableDomain.h
Source/WebCore/testing/ServiceWorkerInternals.cpp
Source/WebCore/testing/ServiceWorkerInternals.h
Source/WebCore/testing/ServiceWorkerInternals.idl
Source/WebCore/workers/service/SWClientConnection.h
Source/WebCore/workers/service/context/SWContextManager.h
Source/WebCore/workers/service/server/SWServer.cpp
Source/WebCore/workers/service/server/SWServer.h
Source/WebCore/workers/service/server/SWServerToContextConnection.h
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.h
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.messages.in
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp
Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h
Source/WebKit/UIProcess/ServiceWorkerProcessProxy.cpp
Source/WebKit/UIProcess/ServiceWorkerProcessProxy.h
Source/WebKit/WebProcess/Storage/WebSWClientConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWClientConnection.h
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h
Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.in
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebProcess.cpp
Source/WebKit/WebProcess/WebProcess.h
Tools/ChangeLog
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestOptions.h

index be72471..f8b7e09 100644 (file)
@@ -1,3 +1,16 @@
+2019-05-14  Youenn Fablet  <youenn@apple.com>
+
+        A service worker process should app nap when all its clients app nap
+        https://bugs.webkit.org/show_bug.cgi?id=185626
+        <rdar://problem/46785908>
+
+        Reviewed by Alex Christensen.
+
+        * http/wpt/service-workers/mac/throttleable-worker.js: Added.
+        * http/wpt/service-workers/mac/throttleable.https-expected.txt: Added.
+        * http/wpt/service-workers/mac/throttleable.https.html: Added.
+        * platform/ios-wk2/TestExpectations:
+
 2019-05-14  Oriol Brufau  <obrufau@igalia.com>
 
         [css-grid] Update grid when changing auto repeat type
diff --git a/LayoutTests/http/wpt/service-workers/mac/throttleable-worker.js b/LayoutTests/http/wpt/service-workers/mac/throttleable-worker.js
new file mode 100644 (file)
index 0000000..ce7855f
--- /dev/null
@@ -0,0 +1,8 @@
+onactivate = (e) => e.waitUntil(clients.claim());
+
+function checkThrottleable(event)
+{
+    event.source.postMessage(self.internals ? self.internals.isThrottleable : "needs internals");
+}
+
+self.addEventListener("message", checkThrottleable);
diff --git a/LayoutTests/http/wpt/service-workers/mac/throttleable.https-expected.txt b/LayoutTests/http/wpt/service-workers/mac/throttleable.https-expected.txt
new file mode 100644 (file)
index 0000000..154bfe1
--- /dev/null
@@ -0,0 +1,5 @@
+
+PASS Setup worker 
+PASS Service worker should not be throttleable 
+PASS Service worker should be throttleable 
+
diff --git a/LayoutTests/http/wpt/service-workers/mac/throttleable.https.html b/LayoutTests/http/wpt/service-workers/mac/throttleable.https.html
new file mode 100644 (file)
index 0000000..c26838c
--- /dev/null
@@ -0,0 +1,63 @@
+<!doctype html><!-- webkit-test-runner [ enableAppNap=true ] -->
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+var scope = "/WebKit/service-workers/mac/throttleable.https.html";
+var activeWorker;
+
+promise_test(async (test) => {
+    var registration = await navigator.serviceWorker.getRegistration(scope);
+    if (registration && registration.scope === scope)
+        await registration.unregister();
+
+    var registration = await navigator.serviceWorker.register("throttleable-worker.js", { scope : scope });
+    activeWorker = registration.active;
+    if (activeWorker)
+        return;
+    activeWorker = registration.installing;
+    return new Promise(resolve => {
+        activeWorker.addEventListener('statechange', () => {
+            if (activeWorker.state === "activated")
+                resolve();
+        });
+    });
+}, "Setup worker");
+
+promise_test(async (test) => {
+    var promise = new Promise((resolve, reject) => {
+        navigator.serviceWorker.addEventListener("message", (event) => {
+            resolve(event.data);
+        });
+    });
+
+    activeWorker.postMessage("checkThrottleable");
+
+    assert_false(await promise);
+}, "Service worker should not be throttleable");
+
+promise_test(async (test) => {
+    if (window.testRunner) {
+        testRunner.setWindowIsKey(false);
+        testRunner.setPageVisibility("hidden");
+    }
+
+    let throttleable = false;
+    for (let cptr = 0; cptr < 1000 && !throttleable; ++cptr) {
+        throttleable = await new Promise((resolve, reject) => {
+            navigator.serviceWorker.addEventListener("message", (event) => {
+                resolve(event.data);
+            });
+            activeWorker.postMessage("checkThrottleable");
+        });
+        await new Promise(resolve => setTimeout(resolve, 50));
+    }
+    assert_true(throttleable);
+}, "Service worker should be throttleable");
+
+</script>
+</body>
+</html>
index a0f1cb5..bc18b67 100644 (file)
@@ -367,6 +367,8 @@ http/tests/security/video-cross-origin-readback.html
 http/tests/websocket/tests/hybi/invalid-subprotocol-characters.html
 http/tests/xmlhttprequest/cross-origin-authorization-with-embedder.html
 
+http/wpt/service-workers/mac/ [ Skip ]
+
 # HTTP tests that are flaky:
 http/tests/navigation/forward-and-cancel.html [ Failure Pass ]
 http/tests/security/xss-DENIED-xsl-external-entity-redirect.xml [ Failure Pass ]
index 24993f0..11afe4f 100644 (file)
@@ -1,5 +1,36 @@
 2019-05-14  Youenn Fablet  <youenn@apple.com>
 
+        A service worker process should app nap when all its clients app nap
+        https://bugs.webkit.org/show_bug.cgi?id=185626
+        <rdar://problem/46785908>
+
+        Reviewed by Alex Christensen.
+
+        Update RegistrableDomain to work with SecurityOriginData.
+        Add internal API to enable accessing to service worker process throttle state.
+
+        Test: http/wpt/service-workers/mac/processSuppression.https.html
+
+        * platform/RegistrableDomain.h:
+        (WebCore::RegistrableDomain::RegistrableDomain):
+        (WebCore::RegistrableDomain::matches const):
+        (WebCore::RegistrableDomain::registrableDomainFromHost):
+        * testing/ServiceWorkerInternals.cpp:
+        (WebCore::ServiceWorkerInternals::isThrottleable const):
+        * testing/ServiceWorkerInternals.h:
+        * testing/ServiceWorkerInternals.idl:
+        * workers/service/SWClientConnection.h:
+        * workers/service/context/SWContextManager.cpp:
+        * workers/service/context/SWContextManager.h:
+        * workers/service/server/SWServer.cpp:
+        (WebCore::SWServer::serverToContextConnectionCreated):
+        * workers/service/server/SWServer.h:
+        (WebCore::SWServer::Connection::server const):
+        (WebCore::SWServer::connections const):
+        * workers/service/server/SWServerToContextConnection.h:
+
+2019-05-14  Youenn Fablet  <youenn@apple.com>
+
         getUserMedia capture changes on iOS after homing out
         https://bugs.webkit.org/show_bug.cgi?id=197707
 
index 448af3f..90aec25 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "PublicSuffix.h"
+#include "SecurityOriginData.h"
 #include <wtf/HashTraits.h>
 #include <wtf/URL.h>
 #include <wtf/text/WTFString.h>
@@ -36,18 +37,15 @@ class RegistrableDomain {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     RegistrableDomain() = default;
+
     explicit RegistrableDomain(const URL& url)
-#if ENABLE(PUBLIC_SUFFIX_LIST)
-        : m_registrableDomain { topPrivatelyControlledDomain(url.host().toString()) }
-#else
-        : m_registrableDomain { url.host().toString() }
-#endif
+        : RegistrableDomain(registrableDomainFromHost(url.host().toString()))
+    {
+    }
+
+    explicit RegistrableDomain(const SecurityOriginData& origin)
+        : RegistrableDomain(registrableDomainFromHost(origin.host))
     {
-        auto hostString = url.host().toString();
-        if (hostString.isEmpty())
-            m_registrableDomain = "nullOrigin"_s;
-        else if (m_registrableDomain.isEmpty())
-            m_registrableDomain = hostString;
     }
 
     bool isEmpty() const { return m_registrableDomain.isEmpty() || m_registrableDomain == "nullOrigin"_s; }
@@ -58,14 +56,12 @@ public:
 
     bool matches(const URL& url) const
     {
-        auto host = url.host();
-        if (host.isEmpty() && m_registrableDomain == "nullOrigin"_s)
-            return true;
-        if (!host.endsWith(m_registrableDomain))
-            return false;
-        if (host.length() == m_registrableDomain.length())
-            return true;
-        return host[host.length() - m_registrableDomain.length() - 1] == '.';
+        return matches(url.host());
+    }
+
+    bool matches(const SecurityOriginData& origin) const
+    {
+        return matches(origin.host);
     }
 
     RegistrableDomain isolatedCopy() const { return RegistrableDomain { m_registrableDomain.isolatedCopy() }; }
@@ -109,6 +105,31 @@ private:
     {
     }
 
+    bool matches(StringView host) const
+    {
+        if (host.isEmpty() && m_registrableDomain == "nullOrigin"_s)
+            return true;
+        if (!host.endsWith(m_registrableDomain))
+            return false;
+        if (host.length() == m_registrableDomain.length())
+            return true;
+        return host[host.length() - m_registrableDomain.length() - 1] == '.';
+    }
+
+    static inline String registrableDomainFromHost(const String& host)
+    {
+#if ENABLE(PUBLIC_SUFFIX_LIST)
+        auto domain = topPrivatelyControlledDomain(host);
+#else
+        auto domain = host;
+#endif
+        if (host.isEmpty())
+            domain = "nullOrigin"_s;
+        else if (domain.isEmpty())
+            domain = host;
+        return domain;
+    }
+
     String m_registrableDomain;
 };
 
index 27417da..5544561 100644 (file)
@@ -96,6 +96,12 @@ String ServiceWorkerInternals::processName() const
 }
 #endif
 
+bool ServiceWorkerInternals::isThrottleable() const
+{
+    auto* connection = SWContextManager::singleton().connection();
+    return connection ? connection->isThrottleable() : true;
+}
+
 } // namespace WebCore
 
 #endif
index 8171c86..8939574 100644 (file)
@@ -51,6 +51,8 @@ public:
 
     String processName() const;
 
+    bool isThrottleable() const;
+
 private:
     explicit ServiceWorkerInternals(ServiceWorkerIdentifier);
 
index 1d131b2..535dd80 100644 (file)
@@ -37,4 +37,5 @@
     sequence<ByteString> fetchResponseHeaderList(FetchResponse response);
 
     readonly attribute DOMString processName;
+    readonly attribute boolean isThrottleable;
 };
index 1fc7162..64f9e23 100644 (file)
@@ -85,6 +85,9 @@ public:
 
     virtual void finishFetchingScriptInServer(const ServiceWorkerFetchResult&) = 0;
 
+    virtual bool isThrottleable() const = 0;
+    virtual void updateThrottleState() = 0;
+
 protected:
     WEBCORE_EXPORT SWClientConnection();
 
index 7607a86..787ad94 100644 (file)
@@ -62,6 +62,8 @@ public:
         virtual void findClientByIdentifier(ServiceWorkerIdentifier, ServiceWorkerClientIdentifier, FindClientByIdentifierCallback&&) = 0;
         virtual void matchAll(ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&, ServiceWorkerClientsMatchAllCallback&&) = 0;
         virtual void claim(ServiceWorkerIdentifier, CompletionHandler<void()>&&) = 0;
+
+        virtual bool isThrottleable() const = 0;
     };
 
     WEBCORE_EXPORT void setConnection(std::unique_ptr<Connection>&&);
index 0200ec6..97b30c1 100644 (file)
@@ -544,6 +544,9 @@ void SWServer::tryInstallContextData(ServiceWorkerContextData&& data)
 
 void SWServer::serverToContextConnectionCreated(SWServerToContextConnection& contextConnection)
 {
+    for (auto& connection : m_connections.values())
+        connection->serverToContextConnectionCreated(contextConnection);
+
     auto pendingContextDatas = m_pendingContextDatas.take(contextConnection.registrableDomain());
     for (auto& data : pendingContextDatas)
         installContextData(data);
index 3ed3da9..6775388 100644 (file)
@@ -87,7 +87,10 @@ public:
         virtual void notifyClientsOfControllerChange(const HashSet<DocumentIdentifier>& contextIdentifiers, const ServiceWorkerData& newController) = 0;
         virtual void registrationReady(uint64_t registrationReadyRequestIdentifier, ServiceWorkerRegistrationData&&) = 0;
 
+        virtual void serverToContextConnectionCreated(SWServerToContextConnection&) = 0;
+
         SWServer& server() { return m_server; }
+        const SWServer& server() const { return m_server; }
 
     protected:
         WEBCORE_EXPORT explicit Connection(SWServer&);
@@ -153,6 +156,8 @@ public:
     WEBCORE_EXPORT void removeConnection(SWServerConnectionIdentifier);
     Connection* connection(SWServerConnectionIdentifier identifier) const { return m_connections.get(identifier); }
 
+    const HashMap<SWServerConnectionIdentifier, std::unique_ptr<Connection>>& connections() const { return m_connections; }
+
     SWOriginStore& originStore() { return m_originStore; }
 
     void scriptContextFailedToStart(const Optional<ServiceWorkerJobDataIdentifier>&, SWServerWorker&, const String& message);
index 2b60f1e..b59c44d 100644 (file)
@@ -76,7 +76,7 @@ public:
     WEBCORE_EXPORT void claim(uint64_t requestIdentifier, ServiceWorkerIdentifier);
     WEBCORE_EXPORT void setScriptResource(ServiceWorkerIdentifier, URL&& scriptURL, String&& script, URL&& responseURL, String&& mimeType);
 
-    static SWServerToContextConnection* connectionForRegistrableDomain(const RegistrableDomain&);
+    WEBCORE_EXPORT static SWServerToContextConnection* connectionForRegistrableDomain(const RegistrableDomain&);
 
     const RegistrableDomain& registrableDomain() const { return m_registrableDomain; }
 
index e99018a..03ba861 100644 (file)
@@ -1,3 +1,50 @@
+2019-05-14  Youenn Fablet  <youenn@apple.com>
+
+        A service worker process should app nap when all its clients app nap
+        https://bugs.webkit.org/show_bug.cgi?id=185626
+        <rdar://problem/46785908>
+
+        Reviewed by Alex Christensen.
+
+        Compute whether a given web process can be throttled on every page throttling change.
+        Send that information to network process which stores that information in WebSWServerConnection.
+        Every WebSWServerToContextConnection throttle state is then computed based on all WebSWServerConnection
+        that have a client that matches the registrable domain of the context connection.
+
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
+        (WebKit::WebSWServerConnection::registerServiceWorkerClient):
+        (WebKit::WebSWServerConnection::unregisterServiceWorkerClient):
+        (WebKit::WebSWServerConnection::hasMatchingClient const):
+        (WebKit::WebSWServerConnection::computeThrottleState const):
+        (WebKit::WebSWServerConnection::setThrottleState):
+        (WebKit::WebSWServerConnection::updateThrottleState):
+        (WebKit::WebSWServerConnection::serverToContextConnectionCreated):
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.h:
+        (WebKit::WebSWServerConnection::isThrottleable const):
+        * NetworkProcess/ServiceWorker/WebSWServerConnection.messages.in:
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
+        (WebKit::WebSWServerToContextConnection::setThrottleState):
+        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
+        (WebKit::WebSWServerToContextConnection::isThrottleable const):
+        * UIProcess/ServiceWorkerProcessProxy.cpp:
+        * UIProcess/ServiceWorkerProcessProxy.h:
+        * WebProcess/Storage/WebSWClientConnection.cpp:
+        (WebKit::WebSWClientConnection::WebSWClientConnection):
+        (WebKit::WebSWClientConnection::updateThrottleState):
+        * WebProcess/Storage/WebSWClientConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
+        (WebKit::WebSWContextManagerConnection::setThrottleState):
+        (WebKit::WebSWContextManagerConnection::isThrottleable const):
+        * WebProcess/Storage/WebSWContextManagerConnection.h:
+        * WebProcess/Storage/WebSWContextManagerConnection.messages.in:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::updateUserActivity):
+        (WebKit::WebPage::isThrottleable const):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebProcess.cpp:
+        (WebKit::WebProcess::arePagesThrottleable const):
+        * WebProcess/WebProcess.h:
+
 2019-05-14  Chris Dumez  <cdumez@apple.com>
 
         Crash under WebKit::WebProcessProxy::didBecomeUnresponsive()
index 373aaea..d32cd46 100644 (file)
@@ -51,6 +51,7 @@
 #include <WebCore/ServiceWorkerContextData.h>
 #include <WebCore/ServiceWorkerJobData.h>
 #include <WebCore/ServiceWorkerUpdateViaCache.h>
+#include <wtf/Algorithms.h>
 #include <wtf/MainThread.h>
 
 namespace WebKit {
@@ -278,6 +279,9 @@ void WebSWServerConnection::registerServiceWorkerClient(SecurityOriginData&& top
     auto clientOrigin = ClientOrigin { WTFMove(topOrigin), SecurityOriginData::fromURL(data.url) };
     m_clientOrigins.add(data.identifier, clientOrigin);
     server().registerServiceWorkerClient(WTFMove(clientOrigin), WTFMove(data), controllingServiceWorkerRegistrationIdentifier, WTFMove(userAgent));
+
+    if (!m_isThrottleable)
+        updateThrottleState();
 }
 
 void WebSWServerConnection::unregisterServiceWorkerClient(const ServiceWorkerClientIdentifier& clientIdentifier)
@@ -288,6 +292,56 @@ void WebSWServerConnection::unregisterServiceWorkerClient(const ServiceWorkerCli
 
     server().unregisterServiceWorkerClient(iterator->value, clientIdentifier);
     m_clientOrigins.remove(iterator);
+
+    if (!m_isThrottleable)
+        updateThrottleState();
+}
+
+bool WebSWServerConnection::hasMatchingClient(const RegistrableDomain& domain) const
+{
+    return WTF::anyOf(m_clientOrigins.values(), [&domain](auto& origin) {
+        return domain.matches(origin.clientOrigin);
+    });
+}
+
+bool WebSWServerConnection::computeThrottleState(const RegistrableDomain& domain) const
+{
+    return WTF::allOf(server().connections().values(), [&domain](auto& serverConnection) {
+        auto& connection = static_cast<WebSWServerConnection&>(*serverConnection);
+        return connection.isThrottleable() || !connection.hasMatchingClient(domain);
+    });
+}
+
+void WebSWServerConnection::setThrottleState(bool isThrottleable)
+{
+    m_isThrottleable = isThrottleable;
+    updateThrottleState();
+}
+
+void WebSWServerConnection::updateThrottleState()
+{
+    HashSet<SecurityOriginData> origins;
+    for (auto& origin : m_clientOrigins.values())
+        origins.add(origin.clientOrigin);
+
+    for (auto& origin : origins) {
+        if (auto* contextConnection = SWServerToContextConnection::connectionForRegistrableDomain(RegistrableDomain { origin })) {
+            auto& connection = static_cast<WebSWServerToContextConnection&>(*contextConnection);
+
+            if (connection.isThrottleable() == m_isThrottleable)
+                continue;
+            bool newThrottleState = computeThrottleState(connection.registrableDomain());
+            if (connection.isThrottleable() == newThrottleState)
+                continue;
+            connection.setThrottleState(newThrottleState);
+        }
+    }
+}
+
+void WebSWServerConnection::serverToContextConnectionCreated(WebCore::SWServerToContextConnection& contextConnection)
+{
+    auto& connection =  static_cast<WebSWServerToContextConnection&>(contextConnection);
+    connection.setThrottleState(computeThrottleState(connection.registrableDomain()));
 }
 
 void WebSWServerConnection::syncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier&& identifier, CompletionHandler<void()>&& completionHandler)
index 1f59d11..90023d6 100644 (file)
@@ -94,6 +94,14 @@ private:
     void unregisterServiceWorkerClient(const WebCore::ServiceWorkerClientIdentifier&);
     void syncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier&&, CompletionHandler<void()>&&);
 
+    void serverToContextConnectionCreated(WebCore::SWServerToContextConnection&) final;
+
+    bool isThrottleable() const { return m_isThrottleable; }
+    bool hasMatchingClient(const WebCore::RegistrableDomain&) const;
+    bool computeThrottleState(const WebCore::RegistrableDomain&) const;
+    void setThrottleState(bool isThrottleable);
+    void updateThrottleState();
+
     IPC::Connection* messageSenderConnection() const final { return m_contentConnection.ptr(); }
     uint64_t messageSenderDestinationID() const final { return identifier().toUInt64(); }
     
@@ -103,6 +111,7 @@ private:
     Ref<IPC::Connection> m_contentConnection;
     Ref<NetworkProcess> m_networkProcess;
     HashMap<WebCore::ServiceWorkerClientIdentifier, WebCore::ClientOrigin> m_clientOrigins;
+    bool m_isThrottleable { true };
 };
 
 } // namespace WebKit
index e3bd740..7c50e62 100644 (file)
@@ -44,6 +44,8 @@ messages -> WebSWServerConnection {
     UnregisterServiceWorkerClient(struct WebCore::ServiceWorkerClientIdentifier identifier)
 
     SyncTerminateWorkerFromClient(WebCore::ServiceWorkerIdentifier workerIdentifier) -> () Synchronous
+
+    SetThrottleState(bool isThrottleable)
 }
 
 #endif // ENABLE(SERVICE_WORKER)
index 3752e56..e782821 100644 (file)
@@ -115,6 +115,12 @@ void WebSWServerToContextConnection::connectionMayNoLongerBeNeeded()
     m_networkProcess->swContextConnectionMayNoLongerBeNeeded(*this);
 }
 
+void WebSWServerToContextConnection::setThrottleState(bool isThrottleable)
+{
+    m_isThrottleable = isThrottleable;
+    send(Messages::WebSWContextManagerConnection::SetThrottleState { isThrottleable });
+}
+
 void WebSWServerToContextConnection::terminate()
 {
     send(Messages::WebSWContextManagerConnection::TerminateProcess());
index 82905ea..dc95efa 100644 (file)
@@ -71,6 +71,9 @@ public:
 
     void didReceiveFetchTaskMessage(IPC::Connection&, IPC::Decoder&);
 
+    void setThrottleState(bool isThrottleable);
+    bool isThrottleable() const { return m_isThrottleable; }
+
 private:
     WebSWServerToContextConnection(NetworkProcess&, const WebCore::RegistrableDomain&, Ref<IPC::Connection>&&);
     ~WebSWServerToContextConnection();
@@ -97,6 +100,7 @@ private:
     
     HashMap<ServiceWorkerFetchTask::Identifier, WebCore::FetchIdentifier> m_ongoingFetchIdentifiers;
     HashMap<WebCore::FetchIdentifier, Ref<ServiceWorkerFetchTask>> m_ongoingFetches;
+    bool m_isThrottleable { true };
 }; // class WebSWServerToContextConnection
 
 } // namespace WebKit
index f1ff2f1..5a14fbe 100644 (file)
@@ -106,15 +106,6 @@ void ServiceWorkerProcessProxy::didReceiveAuthenticationChallenge(uint64_t pageI
     challenge->listener().completeChallenge(AuthenticationChallengeDisposition::PerformDefaultHandling);
 }
 
-void ServiceWorkerProcessProxy::didFinishLaunching(ProcessLauncher* launcher, IPC::Connection::Identifier connectionIdentifier)
-{
-    WebProcessProxy::didFinishLaunching(launcher, connectionIdentifier);
-
-    // Prevent App Nap for Service Worker processes.
-    // FIXME: Ideally, the Service Worker process would app nap when all its clients app nap (http://webkit.org/b/185626).
-    setProcessSuppressionEnabled(false);
-}
-
 } // namespace WebKit
 
 #endif // ENABLE(SERVICE_WORKER)
index 6f759e9..93245f0 100644 (file)
@@ -54,9 +54,6 @@ private:
     // AuxiliaryProcessProxy
     void getLaunchOptions(ProcessLauncher::LaunchOptions&) final;
 
-    // ProcessLauncher::Client
-    void didFinishLaunching(ProcessLauncher*, IPC::Connection::Identifier) final;
-
     bool isServiceWorkerProcess() const final { return true; }
 
     ServiceWorkerProcessProxy(WebProcessPool&, const WebCore::RegistrableDomain&, WebsiteDataStore&);
index 7905b63..bef0422 100644 (file)
@@ -60,6 +60,7 @@ WebSWClientConnection::WebSWClientConnection(IPC::Connection& connection, Sessio
     bool result = sendSync(Messages::NetworkConnectionToWebProcess::EstablishSWServerConnection(sessionID), Messages::NetworkConnectionToWebProcess::EstablishSWServerConnection::Reply(m_identifier));
 
     ASSERT_UNUSED(result, result);
+    updateThrottleState();
 }
 
 WebSWClientConnection::~WebSWClientConnection()
@@ -231,6 +232,12 @@ void WebSWClientConnection::syncTerminateWorker(ServiceWorkerIdentifier identifi
     sendSync(Messages::WebSWServerConnection::SyncTerminateWorkerFromClient(identifier), Messages::WebSWServerConnection::SyncTerminateWorkerFromClient::Reply());
 }
 
+void WebSWClientConnection::updateThrottleState()
+{
+    m_isThrottleable = WebProcess::singleton().areAllPagesThrottleable();
+    send(Messages::WebSWServerConnection::SetThrottleState { m_isThrottleable });
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(SERVICE_WORKER)
index 63e18d5..5177a34 100644 (file)
@@ -87,6 +87,8 @@ private:
     void getRegistrations(WebCore::SecurityOriginData&& topOrigin, const URL& clientURL, GetRegistrationsCallback&&) final;
 
     void didResolveRegistrationPromise(const WebCore::ServiceWorkerRegistrationKey&) final;
+    void updateThrottleState() final;
+    bool isThrottleable() const final { return m_isThrottleable; }
 
     void scheduleStorageJob(const WebCore::ServiceWorkerJobData&);
 
@@ -109,7 +111,7 @@ private:
     HashMap<uint64_t, GetRegistrationsCallback> m_ongoingGetRegistrationsTasks;
     HashMap<uint64_t, WhenRegistrationReadyCallback> m_ongoingRegistrationReadyTasks;
     Deque<WTF::Function<void()>> m_tasksPendingOriginImport;
-
+    bool m_isThrottleable { true };
 }; // class WebSWServerConnection
 
 } // namespace WebKit
index 4d9a654..34b5a64 100644 (file)
@@ -364,6 +364,18 @@ void WebSWContextManagerConnection::terminateProcess()
     _exit(EXIT_SUCCESS);
 }
 
+void WebSWContextManagerConnection::setThrottleState(bool isThrottleable)
+{
+    RELEASE_LOG(ServiceWorker, "Service worker throttleable state is set to %d", isThrottleable);
+    m_isThrottleable = isThrottleable;
+    WebProcess::singleton().setProcessSuppressionEnabled(isThrottleable);
+}
+
+bool WebSWContextManagerConnection::isThrottleable() const
+{
+    return m_isThrottleable;
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(SERVICE_WORKER)
index f71af45..6ce83d1 100644 (file)
@@ -73,6 +73,7 @@ private:
     void claim(WebCore::ServiceWorkerIdentifier, CompletionHandler<void()>&&) final;
     void skipWaiting(WebCore::ServiceWorkerIdentifier, Function<void()>&&) final;
     void setScriptResource(WebCore::ServiceWorkerIdentifier, const URL&, const WebCore::ServiceWorkerContextData::ImportedScript&) final;
+    bool isThrottleable() const final;
 
     // IPC messages.
     void serviceWorkerStartedWithMessage(Optional<WebCore::ServiceWorkerJobDataIdentifier>, WebCore::ServiceWorkerIdentifier, const String& exceptionMessage) final;
@@ -91,6 +92,7 @@ private:
     void didFinishSkipWaiting(uint64_t callbackID);
     void setUserAgent(String&& userAgent);
     NO_RETURN void terminateProcess();
+    void setThrottleState(bool isThrottleable);
 
     Ref<IPC::Connection> m_connectionToNetworkProcess;
     uint64_t m_pageGroupID;
@@ -106,6 +108,7 @@ private:
     HashMap<uint64_t, WTF::Function<void()>> m_skipWaitingRequests;
     uint64_t m_previousRequestIdentifier { 0 };
     String m_userAgent;
+    bool m_isThrottleable { true };
 };
 
 } // namespace WebKit
index 96350c0..e15b02f 100644 (file)
@@ -39,6 +39,7 @@ messages -> WebSWContextManagerConnection {
     SetUserAgent(String userAgent)
     UpdatePreferencesStore(struct WebKit::WebPreferencesStore store)
     TerminateProcess()
+    SetThrottleState(bool isThrottleable)
 }
 
 #endif
index 0104e00..0ed89d0 100644 (file)
 #include <WebCore/ResourceRequest.h>
 #include <WebCore/ResourceResponse.h>
 #include <WebCore/RuntimeEnabledFeatures.h>
+#include <WebCore/SWClientConnection.h>
 #include <WebCore/SchemeRegistry.h>
 #include <WebCore/ScriptController.h>
 #include <WebCore/SerializedScriptValue.h>
@@ -765,17 +766,29 @@ void WebPage::reinitializeWebPage(WebPageCreationParameters&& parameters)
 
 void WebPage::updateThrottleState()
 {
-    bool isActive = m_activityState.containsAny({ ActivityState::IsLoading, ActivityState::IsAudible, ActivityState::IsCapturingMedia, ActivityState::WindowIsActive });
-    bool isVisuallyIdle = m_activityState.contains(ActivityState::IsVisuallyIdle);
-
-    bool shouldAllowAppNap = m_isAppNapEnabled && !isActive && isVisuallyIdle;
+    bool isThrottleable = this->isThrottleable();
 
     // The UserActivity prevents App Nap. So if we want to allow App Nap of the page, stop the activity.
     // If the page should not be app nap'd, start it.
-    if (shouldAllowAppNap)
+    if (isThrottleable)
         m_userActivity.stop();
     else
         m_userActivity.start();
+
+#if ENABLE(SERVICE_WORKER)
+    if (auto* connection = ServiceWorkerProvider::singleton().existingServiceWorkerConnectionForSession(sessionID())) {
+        if (isThrottleable != connection->isThrottleable())
+            connection->updateThrottleState();
+    }
+#endif
+}
+
+bool WebPage::isThrottleable() const
+{
+    bool isActive = m_activityState.containsAny({ ActivityState::IsLoading, ActivityState::IsAudible, ActivityState::IsCapturingMedia, ActivityState::WindowIsActive });
+    bool isVisuallyIdle = m_activityState.contains(ActivityState::IsVisuallyIdle);
+
+    return m_isAppNapEnabled && !isActive && isVisuallyIdle;
 }
 
 WebPage::~WebPage()
index a38eb8b..ec15c43 100644 (file)
@@ -1191,6 +1191,7 @@ public:
 
     void updateIntrinsicContentSizeIfNeeded(const WebCore::IntSize&);
     void scheduleFullEditorStateUpdate();
+    bool isThrottleable() const;
 
 private:
     WebPage(uint64_t pageID, WebPageCreationParameters&&);
index 2528387..97b8305 100644 (file)
 #include <WebCore/ServiceWorkerContextData.h>
 #include <WebCore/Settings.h>
 #include <WebCore/UserGestureIndicator.h>
+#include <wtf/Algorithms.h>
 #include <wtf/Language.h>
 #include <wtf/ProcessPrivilege.h>
 #include <wtf/RunLoop.h>
@@ -1907,4 +1908,11 @@ void WebProcess::unblockAccessibilityServer(const SandboxExtension::Handle& hand
 }
 #endif
 
+bool WebProcess::areAllPagesThrottleable() const
+{
+    return WTF::allOf(m_pageMap.values(), [](auto& page) {
+        return page->isThrottleable();
+    });
+}
+
 } // namespace WebKit
index 30bd59a..4b6ddc6 100644 (file)
@@ -269,6 +269,8 @@ public:
     void setMediaMIMETypes(const Vector<String>);
 #endif
 
+    bool areAllPagesThrottleable() const;
+
 private:
     WebProcess();
     ~WebProcess();
index c4f34e1..79e91b5 100644 (file)
@@ -1,3 +1,19 @@
+2019-05-14  Youenn Fablet  <youenn@apple.com>
+
+        A service worker process should app nap when all its clients app nap
+        https://bugs.webkit.org/show_bug.cgi?id=185626
+        <rdar://problem/46785908>
+
+        Reviewed by Alex Christensen.
+
+        Allow to enable app nap through test header.
+
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::resetPreferencesToConsistentValues):
+        (WTR::updateTestOptionsFromTestHeader):
+        * WebKitTestRunner/TestOptions.h:
+        (WTR::TestOptions::hasSameInitializationOptions const):
+
 2019-05-14  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r245281.
index 8cf8a27..2bbacb1 100644 (file)
@@ -784,7 +784,7 @@ void TestController::resetPreferencesToConsistentValues(const TestOptions& optio
         WKPreferencesSetInternalDebugFeatureForKey(preferences, internalDebugFeature.value, toWK(internalDebugFeature.key).get());
 
     WKPreferencesSetProcessSwapOnNavigationEnabled(preferences, options.contextOptions.shouldEnableProcessSwapOnNavigation());
-    WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, false);
+    WKPreferencesSetPageVisibilityBasedProcessSuppressionEnabled(preferences, options.enableAppNap);
     WKPreferencesSetOfflineWebApplicationCacheEnabled(preferences, true);
     WKPreferencesSetSubpixelAntialiasedLayerTextEnabled(preferences, false);
     WKPreferencesSetXSSAuditorEnabled(preferences, false);
@@ -1397,6 +1397,8 @@ static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std:
             testOptions.contextOptions.ignoreSynchronousMessagingTimeouts = parseBooleanTestHeaderValue(value);
         else if (key == "shouldUseModernCompatibilityMode")
             testOptions.shouldUseModernCompatibilityMode = parseBooleanTestHeaderValue(value);
+        else if (key == "enableAppNap")
+            testOptions.enableAppNap = parseBooleanTestHeaderValue(value);
         pairStart = pairEnd + 1;
     }
 }
index c120484..ec63219 100644 (file)
@@ -92,6 +92,7 @@ struct TestOptions {
     bool shouldHandleRunOpenPanel { true };
     bool shouldPresentPopovers { true };
     bool shouldUseModernCompatibilityMode { false };
+    bool enableAppNap { false };
 
     double contentInsetTop { 0 };
 
@@ -144,7 +145,8 @@ struct TestOptions {
             || shouldHandleRunOpenPanel != options.shouldHandleRunOpenPanel
             || shouldPresentPopovers != options.shouldPresentPopovers
             || contentInsetTop != options.contentInsetTop
-            || shouldUseModernCompatibilityMode != options.shouldUseModernCompatibilityMode)
+            || shouldUseModernCompatibilityMode != options.shouldUseModernCompatibilityMode
+            || enableAppNap != options.enableAppNap)
             return false;
 
         if (!contextOptions.hasSameInitializationOptions(options.contextOptions))