Changeset 291467 in webkit


Ignore:
Timestamp:
Mar 18, 2022 12:33:15 AM (4 months ago)
Author:
youenn@apple.com
Message:

Keep service workers alive when they are inspected even though they should be terminated
https://bugs.webkit.org/show_bug.cgi?id=237827
<rdar://88313935>

Reviewed by Alex Christensen.

Source/WebCore:

Store in SWServerWorker whether a worker is inspected or is processing push events.
In that case, we delay termination of workers until it is no longer inspected or no longer processing push events.
Two code paths are happening:

  1. A service worker was triggered with service worker clients, and all service worker clients are removed. At that point, we were previously terminating service workers after a delay. Instead, we now only terminate workers that are no longer inspected or no longer processing push events. We reschedule the timer to continue trying removing the context connection.
  2. A service worker is not stopped by removal of service worker clients. In that case, we need to terminate the service workers when inspected and/or push counter gets back to regular (not inspected, no push counter). When terminating such a service worker, we try removing the context connection as well.

To make sure SWServerWorker knows whether inspectable or not, we add connection support to transmit whether inspected from WebProcess.
ServiceWorkerInspectorProxy is responsible to update the inspectable value.
Introduce internals API to set inspected state of a service worker.

Covered by new API tests.

  • testing/ServiceWorkerInternals.cpp:
  • testing/ServiceWorkerInternals.h:
  • testing/ServiceWorkerInternals.idl:
  • workers/service/context/SWContextManager.cpp:
  • workers/service/context/SWContextManager.h:
  • workers/service/context/ServiceWorkerInspectorProxy.cpp:
  • workers/service/server/SWServer.cpp:
  • workers/service/server/SWServer.h:
  • workers/service/server/SWServerToContextConnection.cpp:
  • workers/service/server/SWServerToContextConnection.h:
  • workers/service/server/SWServerWorker.cpp:
  • workers/service/server/SWServerWorker.h:

Source/WebKit:

  • NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
  • WebProcess/Storage/WebSWContextManagerConnection.cpp:
  • WebProcess/Storage/WebSWContextManagerConnection.h:

Tools:

  • TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm:
Location:
trunk
Files:
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r291464 r291467  
     12022-03-18  Youenn Fablet  <youenn@apple.com>
     2
     3        Keep service workers alive when they are inspected even though they should be terminated
     4        https://bugs.webkit.org/show_bug.cgi?id=237827
     5        <rdar://88313935>
     6
     7        Reviewed by Alex Christensen.
     8
     9        Store in SWServerWorker whether a worker is inspected or is processing push events.
     10        In that case, we delay termination of workers until it is no longer inspected or no longer processing push events.
     11        Two code paths are happening:
     12        1. A service worker was triggered with service worker clients, and all service worker clients are removed.
     13           At that point, we were previously terminating service workers after a delay.
     14           Instead, we now only terminate workers that are no longer inspected or no longer processing push events.
     15           We reschedule the timer to continue trying removing the context connection.
     16        2. A service worker is not stopped by removal of service worker clients. In that case, we need to terminate
     17           the service workers when inspected and/or push counter gets back to regular (not inspected, no push counter).
     18           When terminating such a service worker, we try removing the context connection as well.
     19
     20        To make sure SWServerWorker knows whether inspectable or not, we add connection support to transmit whether inspected from WebProcess.
     21        ServiceWorkerInspectorProxy is responsible to update the inspectable value.
     22        Introduce internals API to set inspected state of a service worker.
     23
     24        Covered by new API tests.
     25
     26        * testing/ServiceWorkerInternals.cpp:
     27        * testing/ServiceWorkerInternals.h:
     28        * testing/ServiceWorkerInternals.idl:
     29        * workers/service/context/SWContextManager.cpp:
     30        * workers/service/context/SWContextManager.h:
     31        * workers/service/context/ServiceWorkerInspectorProxy.cpp:
     32        * workers/service/server/SWServer.cpp:
     33        * workers/service/server/SWServer.h:
     34        * workers/service/server/SWServerToContextConnection.cpp:
     35        * workers/service/server/SWServerToContextConnection.h:
     36        * workers/service/server/SWServerWorker.cpp:
     37        * workers/service/server/SWServerWorker.h:
     38
    1392022-03-17  Matt Woodrow  <mattwoodrow@apple.com>
    240
  • trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp

    r288416 r291467  
    200200}
    201201
     202void ServiceWorkerInternals::setAsInspected(bool isInspected)
     203{
     204    SWContextManager::singleton().setAsInspected(m_identifier, isInspected);
     205}
     206
    202207} // namespace WebCore
    203208
  • trunk/Source/WebCore/testing/ServiceWorkerInternals.h

    r288093 r291467  
    7474
    7575    String serviceWorkerClientInternalIdentifier(const ServiceWorkerClient&);
     76    void setAsInspected(bool);
    7677
    7778private:
  • trunk/Source/WebCore/testing/ServiceWorkerInternals.idl

    r289117 r291467  
    5353
    5454    DOMString serviceWorkerClientInternalIdentifier(ServiceWorkerClient client);
     55
     56    undefined setAsInspected(boolean isInspected);
    5557};
  • trunk/Source/WebCore/workers/service/context/SWContextManager.cpp

    r286044 r291467  
    209209}
    210210
     211void SWContextManager::setAsInspected(ServiceWorkerIdentifier identifier, bool isInspected)
     212{
     213    if (m_connection)
     214        m_connection->setAsInspected(identifier, isInspected);
     215}
     216
    211217} // namespace WebCore
    212218
  • trunk/Source/WebCore/workers/service/context/SWContextManager.h

    r289721 r291467  
    7070
    7171        virtual void didFailHeartBeatCheck(ServiceWorkerIdentifier) = 0;
     72        virtual void setAsInspected(ServiceWorkerIdentifier, bool) = 0;
    7273
    7374        virtual bool isThrottleable() const = 0;
     
    111112    static constexpr Seconds syncWorkerTerminationTimeout { 100_ms }; // Only used by layout tests.
    112113
     114    WEBCORE_EXPORT void setAsInspected(ServiceWorkerIdentifier, bool);
     115
    113116private:
    114117    SWContextManager() = default;
  • trunk/Source/WebCore/workers/service/context/ServiceWorkerInspectorProxy.cpp

    r291123 r291467  
    6262    m_channel = &channel;
    6363
     64    SWContextManager::singleton().setAsInspected(m_serviceWorkerThreadProxy.identifier(), true);
    6465    m_serviceWorkerThreadProxy.thread().runLoop().postDebuggerTask([] (ScriptExecutionContext& context) {
    6566        downcast<WorkerGlobalScope>(context).inspectorController().connectFrontend();
     
    7273    m_channel = nullptr;
    7374
     75    SWContextManager::singleton().setAsInspected(m_serviceWorkerThreadProxy.identifier(), false);
    7476    m_serviceWorkerThreadProxy.thread().runLoop().postDebuggerTask([] (ScriptExecutionContext& context) {
    7577        downcast<WorkerGlobalScope>(context).inspectorController().disconnectFrontend(DisconnectReason::InspectorDestroyed);
  • trunk/Source/WebCore/workers/service/server/SWServer.cpp

    r290776 r291467  
    10391039            Vector<SWServerWorker*> workersToTerminate;
    10401040            for (auto& worker : m_runningOrTerminatingWorkers.values()) {
    1041                 if (worker->isRunning() && worker->origin() == clientOrigin)
     1041                if (worker->isRunning() && worker->origin() == clientOrigin && !worker->shouldContinue())
    10421042                    workersToTerminate.append(worker.ptr());
    10431043            }
     
    10451045                worker->terminate();
    10461046
    1047             if (!m_clientsByRegistrableDomain.contains(clientRegistrableDomain)) {
    1048                 if (auto* connection = contextConnectionForRegistrableDomain(clientRegistrableDomain)) {
    1049                     removeContextConnection(*connection);
    1050                     connection->connectionIsNoLongerNeeded();
    1051                 }
     1047            if (removeContextConnectionIfPossible(clientRegistrableDomain) == ShouldDelayRemoval::Yes) {
     1048                auto iterator = m_clientIdentifiersPerOrigin.find(clientOrigin);
     1049                ASSERT(iterator != m_clientIdentifiersPerOrigin.end());
     1050                iterator->value.terminateServiceWorkersTimer->startOneShot(m_isProcessTerminationDelayEnabled ? defaultTerminationDelay : defaultPushMessageDuration);
     1051                return;
    10521052            }
    10531053
     
    10731073
    10741074    m_clientToControllingRegistration.remove(registrationIterator);
     1075}
     1076
     1077SWServer::ShouldDelayRemoval SWServer::removeContextConnectionIfPossible(const RegistrableDomain& domain)
     1078{
     1079    if (m_clientsByRegistrableDomain.contains(domain))
     1080        return ShouldDelayRemoval::No;
     1081
     1082    auto* connection = contextConnectionForRegistrableDomain(domain);
     1083    if (!connection)
     1084        return ShouldDelayRemoval::No;
     1085
     1086    for (auto& worker : m_runningOrTerminatingWorkers.values()) {
     1087        if (worker->isRunning() && worker->registrableDomain() == domain && worker->shouldContinue())
     1088            return ShouldDelayRemoval::Yes;
     1089    }
     1090
     1091    removeContextConnection(*connection);
     1092    connection->connectionIsNoLongerNeeded();
     1093    return ShouldDelayRemoval::No;
    10751094}
    10761095
     
    12821301
    12831302            auto serviceWorkerIdentifier = worker->identifier();
    1284             auto terminateWorkerTimer = makeUnique<Timer>([worker = WTFMove(worker)] {
    1285                 RELEASE_LOG_ERROR(ServiceWorker, "Terminating service worker as processing push event took too much time");
    1286                 worker->terminate();
     1303
     1304            worker->incrementPushEventCounter();
     1305            auto terminateWorkerTimer = makeUnique<Timer>([worker] {
     1306                RELEASE_LOG_ERROR(ServiceWorker, "Service worker is taking too much time to process a push event");
     1307                worker->decrementPushEventCounter();
    12871308            });
    1288             terminateWorkerTimer->startOneShot(weakThis && weakThis->m_isProcessTerminationDelayEnabled ? defaultTerminationDelay : 2_s);
    1289             connectionOrStatus.value()->firePushEvent(serviceWorkerIdentifier, data, [callback = WTFMove(callback), terminateWorkerTimer = WTFMove(terminateWorkerTimer)](bool succeeded) mutable {
    1290                 if (terminateWorkerTimer->isActive())
     1309            terminateWorkerTimer->startOneShot(weakThis && weakThis->m_isProcessTerminationDelayEnabled ? defaultTerminationDelay : defaultPushMessageDuration);
     1310            connectionOrStatus.value()->firePushEvent(serviceWorkerIdentifier, data, [callback = WTFMove(callback), terminateWorkerTimer = WTFMove(terminateWorkerTimer), worker = WTFMove(worker)](bool succeeded) mutable {
     1311                if (terminateWorkerTimer->isActive()) {
     1312                    worker->decrementPushEventCounter();
    12911313                    terminateWorkerTimer->stop();
     1314                }
    12921315
    12931316                callback(succeeded);
  • trunk/Source/WebCore/workers/service/server/SWServer.h

    r290776 r291467  
    219219
    220220    static constexpr Seconds defaultTerminationDelay = 10_s;
     221    static constexpr Seconds defaultPushMessageDuration = 2_s;
    221222
    222223    LastNavigationWasAppInitiated clientIsAppInitiatedForRegistrableDomain(const RegistrableDomain&);
     
    231232
    232233    void forEachServiceWorker(const Function<bool(const SWServerWorker&)>&) const;
     234    bool hasClientsWithOrigin(const ClientOrigin& origin) { return m_clientIdentifiersPerOrigin.contains(origin); }
     235
     236    enum class ShouldDelayRemoval : bool { No, Yes };
     237    ShouldDelayRemoval removeContextConnectionIfPossible(const RegistrableDomain&);
    233238
    234239private:
  • trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp

    r290776 r291467  
    138138}
    139139
     140void SWServerToContextConnection::setAsInspected(ServiceWorkerIdentifier identifier, bool isInspected)
     141{
     142    if (auto* worker = SWServerWorker::existingWorkerForIdentifier(identifier))
     143        worker->setAsInspected(isInspected);
     144}
     145
    140146void SWServerToContextConnection::terminateWhenPossible()
    141147{
  • trunk/Source/WebCore/workers/service/server/SWServerToContextConnection.h

    r290776 r291467  
    8080    WEBCORE_EXPORT void setScriptResource(ServiceWorkerIdentifier, URL&& scriptURL, ServiceWorkerContextData::ImportedScript&&);
    8181    WEBCORE_EXPORT void didFailHeartBeatCheck(ServiceWorkerIdentifier);
     82    WEBCORE_EXPORT void setAsInspected(ServiceWorkerIdentifier, bool);
    8283    WEBCORE_EXPORT void findClientByVisibleIdentifier(ServiceWorkerIdentifier, const String& clientIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
    8384
  • trunk/Source/WebCore/workers/service/server/SWServerWorker.cpp

    r288093 r291467  
    398398}
    399399
     400void SWServerWorker::decrementPushEventCounter()
     401{
     402    ASSERT(m_pushEventCounter);
     403    --m_pushEventCounter;
     404    terminateIfPossible();
     405}
     406
     407void SWServerWorker::setAsInspected(bool isInspected)
     408{
     409    m_isInspected = isInspected;
     410    terminateIfPossible();
     411}
     412
     413void SWServerWorker::terminateIfPossible()
     414{
     415    if (m_pushEventCounter || m_isInspected || !m_server || m_server->hasClientsWithOrigin(origin()))
     416        return;
     417
     418    terminate();
     419    m_server->removeContextConnectionIfPossible(registrableDomain());
     420}
     421
    400422} // namespace WebCore
    401423
  • trunk/Source/WebCore/workers/service/server/SWServerWorker.h

    r288974 r291467  
    136136    WorkerThreadMode workerThreadMode() const;
    137137
     138    void incrementPushEventCounter() { ++m_pushEventCounter; }
     139    void decrementPushEventCounter();
     140    void setAsInspected(bool);
     141
     142    bool shouldContinue() const { return !!m_pushEventCounter || m_isInspected; }
     143
    138144private:
    139145    SWServerWorker(SWServer&, SWServerRegistration&, const URL&, const ScriptBuffer&, const CertificateInfo&, const ContentSecurityPolicyResponseHeaders&, const CrossOriginEmbedderPolicy&, String&& referrerPolicy, WorkerType, ServiceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript>&&);
     
    145151    void terminationTimerFired();
    146152    void callTerminationCallbacks();
     153    void terminateIfPossible();
    147154
    148155    WeakPtr<SWServer> m_server;
     
    167174    Timer m_terminationTimer;
    168175    LastNavigationWasAppInitiated m_lastNavigationWasAppInitiated;
     176    int m_pushEventCounter { 0 };
     177    bool m_isInspected { false };
    169178};
    170179
  • trunk/Source/WebKit/ChangeLog

    r291465 r291467  
     12022-03-18  Youenn Fablet  <youenn@apple.com>
     2
     3        Keep service workers alive when they are inspected even though they should be terminated
     4        https://bugs.webkit.org/show_bug.cgi?id=237827
     5        <rdar://88313935>
     6
     7        Reviewed by Alex Christensen.
     8
     9        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
     10        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
     11        * WebProcess/Storage/WebSWContextManagerConnection.h:
     12
    1132022-03-17  Alex Christensen  <achristensen@webkit.org>
    214
  • trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in

    r290903 r291467  
    3939    PostMessageToServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier destination, struct WebCore::MessageWithMessagePorts message, WebCore::ServiceWorkerIdentifier source, String sourceOrigin)
    4040    DidFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier identifier)
     41    SetAsInspected(WebCore::ServiceWorkerIdentifier identifier, bool isInspected)
    4142}
    4243
  • trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp

    r290394 r291467  
    373373}
    374374
     375void WebSWContextManagerConnection::setAsInspected(WebCore::ServiceWorkerIdentifier identifier, bool isInspected)
     376{
     377    m_connectionToNetworkProcess->send(Messages::WebSWServerToContextConnection::SetAsInspected { identifier, isInspected }, 0);
     378}
     379
    375380} // namespace WebCore
    376381
  • trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h

    r289721 r291467  
    8686    bool isThrottleable() const final;
    8787    void didFailHeartBeatCheck(WebCore::ServiceWorkerIdentifier) final;
     88    void setAsInspected(WebCore::ServiceWorkerIdentifier, bool) final;
    8889
    8990    // IPC messages.
  • trunk/Tools/ChangeLog

    r291454 r291467  
     12022-03-18  Youenn Fablet  <youenn@apple.com>
     2
     3        Keep service workers alive when they are inspected even though they should be terminated
     4        https://bugs.webkit.org/show_bug.cgi?id=237827
     5        <rdar://88313935>
     6
     7        Reviewed by Alex Christensen.
     8
     9        * TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm:
     10
    1112022-03-17  Jonathan Bedard  <JonWBedard@gmail.com>
    212
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/PushAPI.mm

    r290815 r291467  
    368368    clearWebsiteDataStore([configuration websiteDataStore]);
    369369}
     370
     371#if WK_HAVE_C_SPI
     372
     373static const char* pushEventsAndInspectedServiceWorkerScriptBytes = R"SWRESOURCE(
     374let port;
     375self.addEventListener("message", (event) => {
     376    port = event.data.port;
     377    port.postMessage(self.internals ? "Ready" : "No internals");
     378});
     379self.addEventListener("push", (event) => {
     380    self.registration.showNotification("notification");
     381    if (event.data.text() === 'first') {
     382        self.internals.setAsInspected(true);
     383        self.previousMessageData = 'first';
     384        return;
     385    }
     386    if (event.data.text() === 'second') {
     387        self.internals.setAsInspected(false);
     388        if (self.previousMessageData !== 'first')
     389            event.waitUntil(Promise.reject('expected first'));
     390        return;
     391    }
     392    if (event.data.text() === 'third') {
     393        if (self.previousMessageData !== undefined)
     394            event.waitUntil(Promise.reject('expected undefined'));
     395        return;
     396    }
     397    self.previousMessageData = event.data.text();
     398    event.waitUntil(Promise.reject('expected a known message'));
     399});
     400)SWRESOURCE";
     401
     402TEST(PushAPI, pushEventsAndInspectedServiceWorker)
     403{
     404    TestWebKitAPI::HTTPServer server({
     405        { "/", { mainBytes } },
     406        { "/sw.js", { {{ "Content-Type", "application/javascript" }}, pushEventsAndInspectedServiceWorkerScriptBytes } }
     407    }, TestWebKitAPI::HTTPServer::Protocol::Http);
     408
     409    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
     410
     411    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     412    clearWebsiteDataStore([configuration websiteDataStore]);
     413
     414    auto context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
     415    [configuration setProcessPool:(WKProcessPool *)context.get()];
     416
     417    auto provider = TestWebKitAPI::TestNotificationProvider({ [[configuration processPool] _notificationManagerForTesting], WKNotificationManagerGetSharedServiceWorkerNotificationManager() });
     418    provider.setPermission(server.origin(), true);
     419
     420    auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
     421    [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
     422
     423    expectedMessage = "Ready";
     424    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     425    [webView loadRequest:server.request()];
     426
     427    TestWebKitAPI::Util::run(&done);
     428
     429    [webView _close];
     430    webView = nullptr;
     431
     432    terminateNetworkProcessWhileRegistrationIsStored(configuration.get());
     433
     434    // Push event for service worker without any related page.
     435    pushMessageProcessed = false;
     436    pushMessageSuccessful = false;
     437    NSString *message = @"first";
     438    [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
     439        pushMessageSuccessful = result;
     440        pushMessageProcessed = true;
     441    }];
     442    TestWebKitAPI::Util::run(&pushMessageProcessed);
     443    EXPECT_TRUE(pushMessageSuccessful);
     444
     445    pushMessageProcessed = false;
     446    pushMessageSuccessful = false;
     447    message = @"second";
     448    [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
     449        pushMessageSuccessful = result;
     450        pushMessageProcessed = true;
     451    }];
     452    TestWebKitAPI::Util::run(&pushMessageProcessed);
     453    EXPECT_TRUE(pushMessageSuccessful);
     454
     455    pushMessageProcessed = false;
     456    pushMessageSuccessful = false;
     457    message = @"third";
     458    [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
     459        pushMessageSuccessful = result;
     460        pushMessageProcessed = true;
     461    }];
     462    TestWebKitAPI::Util::run(&pushMessageProcessed);
     463    EXPECT_TRUE(pushMessageSuccessful);
     464}
     465
     466static const char* inspectedServiceWorkerWithoutPageScriptBytes = R"SWRESOURCE(
     467let port;
     468self.addEventListener("message", (event) => {
     469    port = event.data.port;
     470    port.postMessage(self.internals ? "Ready" : "No internals");
     471});
     472self.addEventListener("push", async (event) => {
     473    self.registration.showNotification("notification");
     474    if (event.data.text() === 'firstmessage-inspected' || event.data.text() === 'firstmessage-not-inspected') {
     475        if (event.data.text() === 'firstmessage-inspected')
     476            self.internals.setAsInspected(true);
     477        if (self.previousMessageData !== undefined)
     478            event.waitUntil(Promise.reject('unexpected state with inspected message'));
     479        self.previousMessageData = 'inspected';
     480        // Wait for client to go away before resolving the event promise;
     481        let resolve;
     482        event.waitUntil(new Promise(r => resolve = r));
     483        while (true) {
     484            const clients = await self.clients.matchAll({ includeUncontrolled : true });
     485            if (!clients.length)
     486                resolve();
     487        }
     488        return;
     489    }
     490    if (event.data.text() === 'not inspected') {
     491        setTimeout(() => self.internals.setAsInspected(false), 10);
     492        if (self.previousMessageData !== 'inspected')
     493            event.waitUntil(Promise.reject('unexpected state with not inspected message'));
     494        self.previousMessageData = 'not inspected';
     495        return;
     496    }
     497    if (event.data.text() === 'last') {
     498        if (self.previousMessageData !== undefined)
     499            event.waitUntil(Promise.reject('unexpected state with last message'));
     500    }
     501});
     502)SWRESOURCE";
     503
     504static void testInspectedServiceWorkerWithoutPage(bool enableServiceWorkerInspection)
     505{
     506    TestWebKitAPI::HTTPServer server({
     507        { "/", { mainBytes } },
     508        { "/sw.js", { {{ "Content-Type", "application/javascript" }}, inspectedServiceWorkerWithoutPageScriptBytes } }
     509    }, TestWebKitAPI::HTTPServer::Protocol::Http);
     510
     511    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
     512
     513    auto dataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
     514    [dataStoreConfiguration setServiceWorkerProcessTerminationDelayEnabled:NO];
     515    auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]);
     516
     517    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     518    configuration.get().websiteDataStore = dataStore.get();
     519    clearWebsiteDataStore([configuration websiteDataStore]);
     520
     521    auto context = adoptWK(TestWebKitAPI::Util::createContextForInjectedBundleTest("InternalsInjectedBundleTest"));
     522    [configuration setProcessPool:(WKProcessPool *)context.get()];
     523
     524    auto provider = TestWebKitAPI::TestNotificationProvider({ [[configuration processPool] _notificationManagerForTesting], WKNotificationManagerGetSharedServiceWorkerNotificationManager() });
     525    provider.setPermission(server.origin(), true);
     526
     527    auto messageHandler = adoptNS([[PushAPIMessageHandlerWithExpectedMessage alloc] init]);
     528    [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
     529
     530    expectedMessage = "Ready";
     531    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     532    [webView loadRequest:server.request()];
     533
     534    TestWebKitAPI::Util::run(&done);
     535
     536    // Push event for service worker without any related page.
     537    pushMessageProcessed = false;
     538    pushMessageSuccessful = false;
     539    NSString *message = @"firstmessage-inspected";
     540    if (!enableServiceWorkerInspection)
     541        message = @"firstmessage-not-inspected";
     542    [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
     543        pushMessageSuccessful = result;
     544        pushMessageProcessed = true;
     545    }];
     546
     547    // We delay so that the push message will happen before the unregistration of the service worker client.
     548    sleep(0.5);
     549
     550    // Closing the web view should not terminate the service worker as service worker is inspected.
     551    [webView _close];
     552    webView = nullptr;
     553
     554    TestWebKitAPI::Util::run(&pushMessageProcessed);
     555    EXPECT_TRUE(pushMessageSuccessful);
     556
     557    // We delay so that the timer to terminate service worker kicks in, at most up to the max push message allowed time, aka 2 seconds.
     558    sleep(3);
     559
     560    // Send message at which point the service worker will not be inspected anymore and will be closed.
     561    pushMessageProcessed = false;
     562    pushMessageSuccessful = false;
     563    message = @"not inspected";
     564    [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
     565        pushMessageSuccessful = result;
     566        pushMessageProcessed = true;
     567    }];
     568    TestWebKitAPI::Util::run(&pushMessageProcessed);
     569    EXPECT_EQ(pushMessageSuccessful, enableServiceWorkerInspection);
     570
     571    // We delay so that the timer to terminate service worker kicks in, at most up to the max push message allowed time, aka 2 seconds.
     572    sleep(3);
     573
     574    pushMessageProcessed = false;
     575    pushMessageSuccessful = false;
     576    message = @"last";
     577    [[configuration websiteDataStore] _processPushMessage:messageDictionary([message dataUsingEncoding:NSUTF8StringEncoding], [server.request() URL]) completionHandler:^(bool result) {
     578        pushMessageSuccessful = result;
     579        pushMessageProcessed = true;
     580    }];
     581
     582    TestWebKitAPI::Util::run(&pushMessageProcessed);
     583    EXPECT_TRUE(pushMessageSuccessful);
     584}
     585
     586TEST(PushAPI, inspectedServiceWorkerWithoutPage)
     587{
     588    bool enableServiceWorkerInspection = true;
     589    testInspectedServiceWorkerWithoutPage(enableServiceWorkerInspection);
     590}
     591
     592TEST(PushAPI, uninspectedServiceWorkerWithoutPage)
     593{
     594    bool enableServiceWorkerInspection = false;
     595    testInspectedServiceWorkerWithoutPage(enableServiceWorkerInspection);
     596}
     597
     598#endif // WK_HAVE_C_SPI
Note: See TracChangeset for help on using the changeset viewer.