Changeset 291938 in webkit


Ignore:
Timestamp:
Mar 26, 2022 8:55:18 AM (4 months ago)
Author:
youenn@apple.com
Message:

Implement ServiceWorkerWindowClient.focus
https://bugs.webkit.org/show_bug.cgi?id=238319
<rdar://90616490>

Reviewed by Brady Eidson.

Source/WebCore:

Add support for service worker focus:

  • Implement user gesture requirement by introducing a flag in ServiceWorkerGlobalScope that is turned on for some time when receiving specific events like notification click event. The max time is currently set to 2 seconds. We might want to improve handling of user activation in non-document contexts but this is a fuzzy area right now so a service worker solution seems good for now.
  • Remove the map of service worker client since we need to create new ones every time.
  • focus will go its context manager that will send an IPC message to the network process to actually trigger focus.

We are missing focused check at promise resolution, this will be done in a follow-up.

Covered by API test.

  • testing/ServiceWorkerInternals.cpp:
  • testing/ServiceWorkerInternals.h:
  • testing/js/WebCoreTestSupport.cpp:
  • workers/service/ServiceWorkerClient.cpp:
  • workers/service/ServiceWorkerClient.h:
  • workers/service/ServiceWorkerClientData.h:
  • workers/service/ServiceWorkerClients.cpp:
  • workers/service/ServiceWorkerClients.h:
  • workers/service/ServiceWorkerGlobalScope.cpp:
  • workers/service/ServiceWorkerGlobalScope.h:
  • workers/service/ServiceWorkerWindowClient.cpp:
  • workers/service/ServiceWorkerWindowClient.h:
  • workers/service/ServiceWorkerWindowClient.idl:
  • workers/service/context/SWContextManager.h:
  • workers/service/context/ServiceWorkerThread.cpp:
  • workers/service/server/SWServer.h:

Source/WebKit:

Support new IPC message flow to focus from a service worker to network process to service worker client process to UIProcess.
Introduce a new delegate _focusWebViewFromServiceWorker as the scope is specific to service worker and usage is different from existing _focusWebView and other delegates.

The overall flow is like this:

  • ServiceWorker process to NetworkProcess to Client WebProcess process to UIProcess (do the actual focus) to NetworkProcess to ServiceWorker process

We might want to directly go from NetworkProcess to UIProcess but we need to handle potential race conditions in that case.

  • NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
  • NetworkProcess/ServiceWorker/WebSWServerConnection.h:
  • NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
  • NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
  • NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
  • UIProcess/API/APIUIClient.h:
  • UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
  • UIProcess/Cocoa/UIDelegate.h:
  • UIProcess/Cocoa/UIDelegate.mm:
  • UIProcess/WebPageProxy.cpp:
  • UIProcess/WebPageProxy.h:
  • UIProcess/WebPageProxy.messages.in:
  • WebProcess/Storage/WebSWClientConnection.cpp:
  • WebProcess/Storage/WebSWClientConnection.h:
  • WebProcess/Storage/WebSWClientConnection.messages.in:
  • WebProcess/Storage/WebSWContextManagerConnection.cpp:
  • WebProcess/Storage/WebSWContextManagerConnection.h:

Source/WTF:

  • Scripts/Preferences/WebPreferencesInternal.yaml:

Tools:

  • TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:

LayoutTests:

Update tests to cover the fact we no longer reuse Client objects.

  • http/tests/workers/service/resources/postmessage-echo-worker.js:
  • http/tests/workers/service/resources/serviceworkerclients-get-worker.js:
  • http/tests/workers/service/serviceworkerclients-matchAll-worker.js:
  • http/tests/workers/service/serviceworkerclients-matchAll.https-expected.txt:
  • http/tests/workers/service/serviceworkerclients-matchAll.https.html:
Location:
trunk
Files:
45 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r291901 r291938  
     12022-03-26  Youenn Fablet  <youenn@apple.com>
     2
     3        Implement ServiceWorkerWindowClient.focus
     4        https://bugs.webkit.org/show_bug.cgi?id=238319
     5        <rdar://90616490>
     6
     7        Reviewed by Brady Eidson.
     8
     9        Update tests to cover the fact we no longer reuse Client objects.
     10
     11        * http/tests/workers/service/resources/postmessage-echo-worker.js:
     12        * http/tests/workers/service/resources/serviceworkerclients-get-worker.js:
     13        * http/tests/workers/service/serviceworkerclients-matchAll-worker.js:
     14        * http/tests/workers/service/serviceworkerclients-matchAll.https-expected.txt:
     15        * http/tests/workers/service/serviceworkerclients-matchAll.https.html:
     16
    1172022-03-25  Alexander Mikhaylenko  <alexm@gnome.org>
    218
  • trunk/LayoutTests/http/tests/workers/service/resources/postmessage-echo-worker.js

    r225403 r291938  
    77            return;
    88        }
    9     } else if (client !== event.source) {
     9    } else if (client.id !== event.source.id) {
    1010        event.source.postMessage("FAIL: client source of the second message is not the same as the first message");
     11        return;
     12    } else if (client === event.source) {
     13        event.source.postMessage("FAIL: client source of the second message is the same object as for the first message");
    1114        return;
    1215    }
  • trunk/LayoutTests/http/tests/workers/service/resources/serviceworkerclients-get-worker.js

    r225427 r291938  
    2020        }
    2121
    22         if (retrievedClient !== client) {
    23             event.source.postMessage("FAIL: client is different from the one retrieved through self.clients.get");
     22        if (retrievedClient === client) {
     23            event.source.postMessage("FAIL: reusing same client object in different self.clients.get calls");
    2424            return;
    2525        }
  • trunk/LayoutTests/http/tests/workers/service/serviceworkerclients-matchAll-worker.js

    r288093 r291938  
    4242{
    4343    try {
     44        if (event.data.test === "checkNewClientObject") {
     45            const clients1 = await self.clients.matchAll({ includeUncontrolled : true });
     46            const clients2 = await self.clients.matchAll({ includeUncontrolled : true });
     47            if (!clients1.length || !clients2.length) {
     48                event.source.postMessage("no clients");
     49                return;
     50            }
     51            for (let client1 of clients1) {
     52                for (let client2 of clients2) {
     53                    if (client1 === client2) {
     54                        event.source.postMessage("FAIL: reusing client objects");
     55                        return;
     56                    }
     57                }
     58            }
     59            event.source.postMessage("PASS");
     60            return;
     61        }
     62
    4463        if (event.data.test !== "checkClientIsUncontrolled") {
    4564            event.source.postMessage("FAIL: received unexpected message from client");
  • trunk/LayoutTests/http/tests/workers/service/serviceworkerclients-matchAll.https-expected.txt

    r267644 r291938  
    11
    22PASS Setup worker
     3PASS Test self.clients.matchAll create new client object every time
    34PASS Test self.clients.matchAll
    45
  • trunk/LayoutTests/http/tests/workers/service/serviceworkerclients-matchAll.https.html

    r288093 r291938  
    2828
    2929promise_test(async (test) => {
     30    var promise = new Promise((resolve, reject) => {
     31        navigator.serviceWorker.addEventListener("message", test.step_func((event) => {
     32            assert_equals(event.data, "PASS");
     33            resolve();
     34        }));
     35    });
     36
     37    activeWorker.postMessage({test: "checkNewClientObject"});
     38    await promise;
     39}, "Test self.clients.matchAll create new client object every time");
     40
     41promise_test(async (test) => {
    3042    const serviceWorkerClientIdentifier = internals.serviceWorkerClientInternalIdentifier(document);
    3143    var promise = new Promise((resolve, reject) => {
  • trunk/Source/WTF/ChangeLog

    r291937 r291938  
     12022-03-26  Youenn Fablet  <youenn@apple.com>
     2
     3        Implement ServiceWorkerWindowClient.focus
     4        https://bugs.webkit.org/show_bug.cgi?id=238319
     5        <rdar://90616490>
     6
     7        Reviewed by Brady Eidson.
     8
     9        * Scripts/Preferences/WebPreferencesInternal.yaml:
     10
    1112022-03-26  Yusuke Suzuki  <ysuzuki@apple.com>
    212
  • trunk/Source/WTF/Scripts/Preferences/WebPreferencesInternal.yaml

    r291473 r291938  
    844844      default: false
    845845
     846ServiceWorkersUserGestureEnabled:
     847  type: bool
     848  humanReadableName: "Validate UserGesture requirements in Service Workers"
     849  humanReadableDescription: "Validate UserGesture requirements in Service Workers"
     850  condition: ENABLE(SERVICE_WORKER)
     851  defaultValue:
     852    WebKitLegacy:
     853      default: false
     854    WebKit:
     855      default: true
     856    WebCore:
     857      default: true
     858
    846859SimpleLineLayoutEnabled:
    847860  type: bool
  • trunk/Source/WebCore/ChangeLog

    r291937 r291938  
     12022-03-26  Youenn Fablet  <youenn@apple.com>
     2
     3        Implement ServiceWorkerWindowClient.focus
     4        https://bugs.webkit.org/show_bug.cgi?id=238319
     5        <rdar://90616490>
     6
     7        Reviewed by Brady Eidson.
     8
     9        Add support for service worker focus:
     10        - Implement user gesture requirement by introducing a flag in ServiceWorkerGlobalScope that is turned on for some time
     11          when receiving specific events like notification click event. The max time is currently set to 2 seconds.
     12          We might want to improve handling of user activation in non-document contexts but this is a fuzzy area right now so a service worker
     13          solution seems good for now.
     14        - Remove the map of service worker client since we need to create new ones every time.
     15        - focus will go its context manager that will send an IPC message to the network process to actually trigger focus.
     16        We are missing focused check at promise resolution, this will be done in a follow-up.
     17
     18        Covered by API test.
     19
     20        * testing/ServiceWorkerInternals.cpp:
     21        * testing/ServiceWorkerInternals.h:
     22        * testing/js/WebCoreTestSupport.cpp:
     23        * workers/service/ServiceWorkerClient.cpp:
     24        * workers/service/ServiceWorkerClient.h:
     25        * workers/service/ServiceWorkerClientData.h:
     26        * workers/service/ServiceWorkerClients.cpp:
     27        * workers/service/ServiceWorkerClients.h:
     28        * workers/service/ServiceWorkerGlobalScope.cpp:
     29        * workers/service/ServiceWorkerGlobalScope.h:
     30        * workers/service/ServiceWorkerWindowClient.cpp:
     31        * workers/service/ServiceWorkerWindowClient.h:
     32        * workers/service/ServiceWorkerWindowClient.idl:
     33        * workers/service/context/SWContextManager.h:
     34        * workers/service/context/ServiceWorkerThread.cpp:
     35        * workers/service/server/SWServer.h:
     36
    1372022-03-26  Yusuke Suzuki  <ysuzuki@apple.com>
    238
  • trunk/Source/WebCore/testing/ServiceWorkerInternals.cpp

    r291467 r291938  
    3535#include "SWContextManager.h"
    3636#include "ServiceWorkerClient.h"
     37#include "ServiceWorkerGlobalScope.h"
    3738#include "ServiceWorkerRegistration.h"
    3839#include <wtf/ProcessID.h>
     
    4041namespace WebCore {
    4142
    42 ServiceWorkerInternals::ServiceWorkerInternals(ServiceWorkerIdentifier identifier)
     43ServiceWorkerInternals::ServiceWorkerInternals(ServiceWorkerGlobalScope& globalScope, ServiceWorkerIdentifier identifier)
    4344    : m_identifier(identifier)
    4445{
     46    globalScope.setIsProcessingUserGestureForTesting(true);
    4547}
    4648
  • trunk/Source/WebCore/testing/ServiceWorkerInternals.h

    r291467 r291938  
    4141class PushSubscription;
    4242class ScriptExecutionContext;
     43class ServiceWorkerGlobalScope;
    4344class ServiceWorkerClient;
    4445
     
    4748class WEBCORE_TESTSUPPORT_EXPORT ServiceWorkerInternals : public RefCounted<ServiceWorkerInternals>, public CanMakeWeakPtr<ServiceWorkerInternals> {
    4849public:
    49     static Ref<ServiceWorkerInternals> create(ServiceWorkerIdentifier identifier) { return adoptRef(*new ServiceWorkerInternals { identifier }); }
     50    static Ref<ServiceWorkerInternals> create(ServiceWorkerGlobalScope& globalScope, ServiceWorkerIdentifier identifier) { return adoptRef(*new ServiceWorkerInternals { globalScope, identifier }); }
    5051    ~ServiceWorkerInternals();
    5152
     
    7778
    7879private:
    79     explicit ServiceWorkerInternals(ServiceWorkerIdentifier);
     80    ServiceWorkerInternals(ServiceWorkerGlobalScope&, ServiceWorkerIdentifier);
    8081
    8182    ServiceWorkerIdentifier m_identifier;
  • trunk/Source/WebCore/testing/js/WebCoreTestSupport.cpp

    r287737 r291938  
    229229        JSLockHolder locker(vm);
    230230        auto* contextWrapper = script->globalScopeWrapper();
    231         contextWrapper->putDirect(vm, Identifier::fromString(vm, Internals::internalsId), toJS(&globalObject, contextWrapper, ServiceWorkerInternals::create(identifier)));
     231        contextWrapper->putDirect(vm, Identifier::fromString(vm, Internals::internalsId), toJS(&globalObject, contextWrapper, ServiceWorkerInternals::create(globalScope, identifier)));
    232232    });
    233233#else
  • trunk/Source/WebCore/workers/service/ServiceWorkerClient.cpp

    r291888 r291938  
    4040namespace WebCore {
    4141
    42 Ref<ServiceWorkerClient> ServiceWorkerClient::getOrCreate(ServiceWorkerGlobalScope& context, ServiceWorkerClientData&& data)
     42Ref<ServiceWorkerClient> ServiceWorkerClient::create(ServiceWorkerGlobalScope& context, ServiceWorkerClientData&& data)
    4343{
    44     if (auto* client = context.serviceWorkerClient(data.identifier)) {
    45         // Temporary fix until we remove reusing of same ServiceWorkerClient objects.
    46         client->m_data = WTFMove(data);
    47         return *client;
    48     }
    49 
    5044    if (data.type == ServiceWorkerClientType::Window)
    5145        return ServiceWorkerWindowClient::create(context, WTFMove(data));
     
    5852    , m_data(WTFMove(data))
    5953{
    60     context.addServiceWorkerClient(*this);
    6154}
    6255
    6356ServiceWorkerClient::~ServiceWorkerClient()
    6457{
    65     if (auto* context = scriptExecutionContext())
    66         downcast<ServiceWorkerGlobalScope>(*context).removeServiceWorkerClient(*this);
    6758}
    6859
  • trunk/Source/WebCore/workers/service/ServiceWorkerClient.h

    r291888 r291938  
    5353    using FrameType = ServiceWorkerClientFrameType;
    5454
    55     static Ref<ServiceWorkerClient> getOrCreate(ServiceWorkerGlobalScope&, ServiceWorkerClientData&&);
     55    static Ref<ServiceWorkerClient> create(ServiceWorkerGlobalScope&, ServiceWorkerClientData&&);
    5656
    5757    ~ServiceWorkerClient();
  • trunk/Source/WebCore/workers/service/ServiceWorkerClientData.h

    r291888 r291938  
    5454    ServiceWorkerClientData isolatedCopy() &&;
    5555
    56     static ServiceWorkerClientData from(ScriptExecutionContext&);
     56    WEBCORE_EXPORT static ServiceWorkerClientData from(ScriptExecutionContext&);
    5757
    5858    template<class Encoder> void encode(Encoder&) const;
  • trunk/Source/WebCore/workers/service/ServiceWorkerClients.cpp

    r291888 r291938  
    4646    }
    4747
    48     promise.resolve<IDLInterface<ServiceWorkerClient>>(ServiceWorkerClient::getOrCreate(scope, WTFMove(*clientData)));
     48    promise.resolve<IDLInterface<ServiceWorkerClient>>(ServiceWorkerClient::create(scope, WTFMove(*clientData)));
    4949}
    5050
     
    5353    auto serviceWorkerIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
    5454
    55     auto promisePointer = promise.ptr();
    56     m_pendingPromises.add(promisePointer, WTFMove(promise));
    57 
    58     callOnMainThread([promisePointer, serviceWorkerIdentifier, id = id.isolatedCopy()] () {
     55    callOnMainThread([promiseIdentifier = addPendingPromise(WTFMove(promise)), serviceWorkerIdentifier, id = id.isolatedCopy()] () {
    5956        auto connection = SWContextManager::singleton().connection();
    60         connection->findClientByVisibleIdentifier(serviceWorkerIdentifier, id, [promisePointer, serviceWorkerIdentifier] (auto&& clientData) {
    61             SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promisePointer, data = crossThreadCopy(WTFMove(clientData))] (auto& context) mutable {
    62                 if (auto promise = context.clients().m_pendingPromises.take(promisePointer))
     57        connection->findClientByVisibleIdentifier(serviceWorkerIdentifier, id, [promiseIdentifier, serviceWorkerIdentifier] (auto&& clientData) {
     58            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promiseIdentifier, data = crossThreadCopy(WTFMove(clientData))] (auto& context) mutable {
     59                if (auto promise = context.clients().takePendingPromise(promiseIdentifier))
    6360                    didFinishGetRequest(context, *promise, WTFMove(data));
    6461            });
     
    7168{
    7269    auto clients = WTF::map(clientsData, [&] (auto&& clientData) {
    73         return ServiceWorkerClient::getOrCreate(scope, WTFMove(clientData));
     70        return ServiceWorkerClient::create(scope, WTFMove(clientData));
    7471    });
    7572    std::sort(clients.begin(), clients.end(), [&] (auto& a, auto& b) {
     
    8178void ServiceWorkerClients::matchAll(ScriptExecutionContext& context, const ClientQueryOptions& options, Ref<DeferredPromise>&& promise)
    8279{
    83     auto promisePointer = promise.ptr();
    84     m_pendingPromises.add(promisePointer, WTFMove(promise));
    85 
    8680    auto serviceWorkerIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
    8781
    88     callOnMainThread([promisePointer, serviceWorkerIdentifier, options] () mutable {
     82    callOnMainThread([promiseIdentifier = addPendingPromise(WTFMove(promise)), serviceWorkerIdentifier, options] () mutable {
    8983        auto connection = SWContextManager::singleton().connection();
    90         connection->matchAll(serviceWorkerIdentifier, options, [promisePointer, serviceWorkerIdentifier] (auto&& clientsData) mutable {
    91             SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promisePointer, clientsData = crossThreadCopy(WTFMove(clientsData))] (auto& scope) mutable {
    92                 if (auto promise = scope.clients().m_pendingPromises.take(promisePointer))
     84        connection->matchAll(serviceWorkerIdentifier, options, [promiseIdentifier, serviceWorkerIdentifier] (auto&& clientsData) mutable {
     85            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promiseIdentifier, clientsData = crossThreadCopy(WTFMove(clientsData))] (auto& scope) mutable {
     86                if (auto promise = scope.clients().takePendingPromise(promiseIdentifier))
    9387                    matchAllCompleted(scope, *promise, WTFMove(clientsData));
    9488            });
     
    10599void ServiceWorkerClients::claim(ScriptExecutionContext& context, Ref<DeferredPromise>&& promise)
    106100{
    107     auto& serviceWorkerGlobalScope = downcast<ServiceWorkerGlobalScope>(context);
     101    auto serviceWorkerIdentifier = downcast<ServiceWorkerGlobalScope>(context).thread().identifier();
    108102
    109     auto serviceWorkerIdentifier = serviceWorkerGlobalScope.thread().identifier();
    110 
    111     auto promisePointer = promise.ptr();
    112     m_pendingPromises.add(promisePointer, WTFMove(promise));
    113 
    114     callOnMainThread([promisePointer, serviceWorkerIdentifier] () mutable {
     103    callOnMainThread([promiseIdentifier = addPendingPromise(WTFMove(promise)), serviceWorkerIdentifier] () mutable {
    115104        auto connection = SWContextManager::singleton().connection();
    116         connection->claim(serviceWorkerIdentifier, [promisePointer, serviceWorkerIdentifier](auto&& result) mutable {
    117             SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promisePointer, result = crossThreadCopy(WTFMove(result))](auto& scope) mutable {
    118                 if (auto promise = scope.clients().m_pendingPromises.take(promisePointer)) {
     105        connection->claim(serviceWorkerIdentifier, [promiseIdentifier, serviceWorkerIdentifier](auto&& result) mutable {
     106            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promiseIdentifier, result = crossThreadCopy(WTFMove(result))](auto& scope) mutable {
     107                if (auto promise = scope.clients().takePendingPromise(promiseIdentifier)) {
    119108                    DOMPromiseDeferred<void> pendingPromise { promise.releaseNonNull() };
    120109                    pendingPromise.settle(WTFMove(result));
     
    125114}
    126115
     116ServiceWorkerClients::PromiseIdentifier ServiceWorkerClients::addPendingPromise(Ref<DeferredPromise>&& promise)
     117{
     118    auto identifier = PromiseIdentifier::generateThreadSafe();
     119    m_pendingPromises.add(identifier, WTFMove(promise));
     120    return identifier;
     121}
     122
     123RefPtr<DeferredPromise> ServiceWorkerClients::takePendingPromise(PromiseIdentifier identifier)
     124{
     125    return m_pendingPromises.take(identifier);
     126}
     127
    127128} // namespace WebCore
    128129
  • trunk/Source/WebCore/workers/service/ServiceWorkerClients.h

    r286012 r291938  
    5555    void claim(ScriptExecutionContext&, Ref<DeferredPromise>&&);
    5656
     57    enum PromiseIdentifierType { };
     58    using PromiseIdentifier = ObjectIdentifier<PromiseIdentifierType>;
     59
     60    PromiseIdentifier addPendingPromise(Ref<DeferredPromise>&&);
     61    RefPtr<DeferredPromise> takePendingPromise(PromiseIdentifier);
     62
    5763private:
    5864    ServiceWorkerClients() = default;
    5965
    60     HashMap<DeferredPromise*, Ref<DeferredPromise>> m_pendingPromises;
     66    HashMap<PromiseIdentifier, Ref<DeferredPromise>> m_pendingPromises;
    6167};
    6268
  • trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp

    r289726 r291938  
    6969    , m_sessionID(sessionID)
    7070    , m_notificationClient(WTFMove(notificationClient))
     71    , m_userGestureTimer(*this, &ServiceWorkerGlobalScope::resetUserGesture)
    7172{
    7273}
     
    135136}
    136137
    137 ServiceWorkerClient* ServiceWorkerGlobalScope::serviceWorkerClient(ScriptExecutionContextIdentifier identifier)
    138 {
    139     return m_clientMap.get(identifier);
    140 }
    141 
    142 void ServiceWorkerGlobalScope::addServiceWorkerClient(ServiceWorkerClient& client)
    143 {
    144     auto result = m_clientMap.add(client.identifier(), &client);
    145     ASSERT_UNUSED(result, result.isNewEntry);
    146 }
    147 
    148 void ServiceWorkerGlobalScope::removeServiceWorkerClient(ServiceWorkerClient& client)
    149 {
    150     auto isRemoved = m_clientMap.remove(client.identifier());
    151     ASSERT_UNUSED(isRemoved, isRemoved);
    152 }
    153 
    154138// https://w3c.github.io/ServiceWorker/#update-service-worker-extended-events-set-algorithm
    155139void ServiceWorkerGlobalScope::updateExtendedEventsSet(ExtendableEvent* newEvent)
     
    225209            case NotificationEventType::Click:
    226210                eventName = eventNames().notificationclickEvent;
     211                downcast<ServiceWorkerGlobalScope>(scope).recordUserGesture();
    227212                break;
    228213            case NotificationEventType::Close:
     
    238223#endif
    239224
     225void ServiceWorkerGlobalScope::recordUserGesture()
     226{
     227    m_isProcessingUserGesture = true;
     228    m_userGestureTimer.startOneShot(userGestureLifetime);
     229}
     230
    240231} // namespace WebCore
    241232
  • trunk/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h

    r290815 r291938  
    6868    ServiceWorkerThread& thread();
    6969
    70     ServiceWorkerClient* serviceWorkerClient(ScriptExecutionContextIdentifier);
    71     void addServiceWorkerClient(ServiceWorkerClient&);
    72     void removeServiceWorkerClient(ServiceWorkerClient&);
    73 
    7470    void updateExtendedEventsSet(ExtendableEvent* newEvent = nullptr);
    7571
     
    9389    void setHasPendingSilentPushEvent(bool value) { m_hasPendingSilentPushEvent = value; }
    9490
     91    constexpr static Seconds userGestureLifetime  { 2_s };
     92    bool isProcessingUserGesture() const { return m_isProcessingUserGesture; }
     93    void recordUserGesture();
     94    void setIsProcessingUserGestureForTesting(bool value) { m_isProcessingUserGesture = value; }
     95
    9596private:
    9697    ServiceWorkerGlobalScope(ServiceWorkerContextData&&, ServiceWorkerData&&, const WorkerParameters&, Ref<SecurityOrigin>&&, ServiceWorkerThread&, Ref<SecurityOrigin>&& topOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, std::unique_ptr<NotificationClient>&&, PAL::SessionID);
     
    104105    std::optional<PAL::SessionID> sessionID() const final { return m_sessionID; }
    105106
     107    void resetUserGesture() { m_isProcessingUserGesture = false; }
     108
    106109    ServiceWorkerContextData m_contextData;
    107110    Ref<ServiceWorkerRegistration> m_registration;
    108111    Ref<ServiceWorker> m_serviceWorker;
    109112    Ref<ServiceWorkerClients> m_clients;
    110     HashMap<ScriptExecutionContextIdentifier, ServiceWorkerClient*> m_clientMap;
    111113    Vector<Ref<ExtendableEvent>> m_extendedEvents;
    112114
     
    116118    std::unique_ptr<NotificationClient> m_notificationClient;
    117119    bool m_hasPendingSilentPushEvent { false };
     120    bool m_isProcessingUserGesture { false };
     121    Timer m_userGestureTimer;
    118122};
    119123
  • trunk/Source/WebCore/workers/service/ServiceWorkerWindowClient.cpp

    r291888 r291938  
    3030
    3131#include "JSDOMPromiseDeferred.h"
     32#include "JSServiceWorkerWindowClient.h"
     33#include "SWContextManager.h"
     34#include "ServiceWorkerClients.h"
     35#include "ServiceWorkerThread.h"
    3236
    3337namespace WebCore {
     
    3842}
    3943
    40 void ServiceWorkerWindowClient::focus(Ref<DeferredPromise>&& promise)
     44void ServiceWorkerWindowClient::focus(ScriptExecutionContext& context, Ref<DeferredPromise>&& promise)
    4145{
    42     promise->reject(Exception { NotSupportedError, "windowClient.focus() is not yet supported"_s });
     46    auto& serviceWorkerContext = downcast<ServiceWorkerGlobalScope>(context);
     47
     48    if (context.settingsValues().serviceWorkersUserGestureEnabled && !serviceWorkerContext.isProcessingUserGesture()) {
     49        promise->reject(Exception { InvalidAccessError, "WindowClient focus requires a user gesture"_s });
     50        return;
     51    }
     52
     53    auto promiseIdentifier = serviceWorkerContext.clients().addPendingPromise(WTFMove(promise));
     54    callOnMainThread([clientIdentifier = identifier(), promiseIdentifier, serviceWorkerIdentifier = serviceWorkerContext.thread().identifier()]() mutable {
     55        SWContextManager::singleton().connection()->focus(clientIdentifier, [promiseIdentifier, serviceWorkerIdentifier](auto result) mutable {
     56            SWContextManager::singleton().postTaskToServiceWorker(serviceWorkerIdentifier, [promiseIdentifier, result = crossThreadCopy(WTFMove(result))](auto& serviceWorkerContext) mutable {
     57                auto promise = serviceWorkerContext.clients().takePendingPromise(promiseIdentifier);
     58                if (!promise)
     59                    return;
     60
     61                // FIXME: Check isFocused state and reject if not focused.
     62                if (!result) {
     63                    promise->reject(Exception { TypeError, "WindowClient focus failed"_s });
     64                    return;
     65                }
     66
     67                promise->template resolve<IDLInterface<ServiceWorkerWindowClient>>(ServiceWorkerWindowClient::create(serviceWorkerContext, WTFMove(*result)));
     68            });
     69        });
     70    });
    4371}
    4472
    45 void ServiceWorkerWindowClient::navigate(const String& url, Ref<DeferredPromise>&& promise)
     73void ServiceWorkerWindowClient::navigate(ScriptExecutionContext&, const String& url, Ref<DeferredPromise>&& promise)
    4674{
    4775    UNUSED_PARAM(url);
  • trunk/Source/WebCore/workers/service/ServiceWorkerWindowClient.h

    r291888 r291938  
    4646    bool isFocused() const { return data().isFocused; }
    4747
    48     void focus(Ref<DeferredPromise>&&);
    49     void navigate(const String& url, Ref<DeferredPromise>&&);
     48    void focus(ScriptExecutionContext&, Ref<DeferredPromise>&&);
     49    void navigate(ScriptExecutionContext&, const String& url, Ref<DeferredPromise>&&);
    5050
    5151private:
  • trunk/Source/WebCore/workers/service/ServiceWorkerWindowClient.idl

    r290304 r291938  
    3333    [ImplementedAs=isFocused] readonly attribute boolean focused;
    3434
    35     [NewObject] Promise<ServiceWorkerWindowClient> focus();
    36     [NewObject] Promise<ServiceWorkerWindowClient> navigate(USVString url);
     35    [NewObject, CallWith=CurrentScriptExecutionContext] Promise<ServiceWorkerWindowClient> focus();
     36    [NewObject, CallWith=CurrentScriptExecutionContext] Promise<ServiceWorkerWindowClient> navigate(USVString url);
    3737};
  • trunk/Source/WebCore/workers/service/context/SWContextManager.h

    r291467 r291938  
    6868        virtual void matchAll(ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&, ServiceWorkerClientsMatchAllCallback&&) = 0;
    6969        virtual void claim(ServiceWorkerIdentifier, CompletionHandler<void(ExceptionOr<void>&&)>&&) = 0;
     70
     71        virtual void focus(ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&) = 0;
    7072
    7173        virtual void didFailHeartBeatCheck(ServiceWorkerIdentifier) = 0;
  • trunk/Source/WebCore/workers/service/context/ServiceWorkerThread.cpp

    r290815 r291938  
    152152        ExtendableMessageEventSource source;
    153153        if (std::holds_alternative<ServiceWorkerClientData>(sourceData)) {
    154             RefPtr<ServiceWorkerClient> sourceClient = ServiceWorkerClient::getOrCreate(serviceWorkerGlobalScope, WTFMove(std::get<ServiceWorkerClientData>(sourceData)));
     154            RefPtr<ServiceWorkerClient> sourceClient = ServiceWorkerClient::create(serviceWorkerGlobalScope, WTFMove(std::get<ServiceWorkerClientData>(sourceData)));
    155155
    156156            RELEASE_ASSERT(!sourceClient->url().protocolIsInHTTPFamily() || !serviceWorkerGlobalScope->url().protocolIsInHTTPFamily() || protocolHostAndPortAreEqual(serviceWorkerGlobalScope->url(), sourceClient->url()));
  • trunk/Source/WebCore/workers/service/server/SWServer.h

    r291888 r291938  
    9292        virtual void notifyClientsOfControllerChange(const HashSet<ScriptExecutionContextIdentifier>& contextIdentifiers, const ServiceWorkerData& newController) = 0;
    9393        virtual void postMessageToServiceWorkerClient(ScriptExecutionContextIdentifier, const MessageWithMessagePorts&, ServiceWorkerIdentifier, const String& sourceOrigin) = 0;
     94        virtual void focusServiceWorkerClient(ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<ServiceWorkerClientData>&&)>&&) = 0;
    9495
    9596        virtual void contextConnectionCreated(SWServerToContextConnection&) = 0;
  • trunk/Source/WebKit/ChangeLog

    r291937 r291938  
     12022-03-26  Youenn Fablet  <youenn@apple.com>
     2
     3        Implement ServiceWorkerWindowClient.focus
     4        https://bugs.webkit.org/show_bug.cgi?id=238319
     5        <rdar://90616490>
     6
     7        Reviewed by Brady Eidson.
     8
     9        Support new IPC message flow to focus from a service worker to network process to service worker client process to UIProcess.
     10        Introduce a new delegate _focusWebViewFromServiceWorker as the scope is specific to service worker and usage is different from existing _focusWebView and other delegates.
     11
     12        The overall flow is like this:
     13        - ServiceWorker process to NetworkProcess to Client WebProcess process to UIProcess (do the actual focus) to NetworkProcess to ServiceWorker process
     14        We might want to directly go from NetworkProcess to UIProcess but we need to handle potential race conditions in that case.
     15
     16        * NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
     17        * NetworkProcess/ServiceWorker/WebSWServerConnection.h:
     18        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp:
     19        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h:
     20        * NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in:
     21        * UIProcess/API/APIUIClient.h:
     22        * UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
     23        * UIProcess/Cocoa/UIDelegate.h:
     24        * UIProcess/Cocoa/UIDelegate.mm:
     25        * UIProcess/WebPageProxy.cpp:
     26        * UIProcess/WebPageProxy.h:
     27        * UIProcess/WebPageProxy.messages.in:
     28        * WebProcess/Storage/WebSWClientConnection.cpp:
     29        * WebProcess/Storage/WebSWClientConnection.h:
     30        * WebProcess/Storage/WebSWClientConnection.messages.in:
     31        * WebProcess/Storage/WebSWContextManagerConnection.cpp:
     32        * WebProcess/Storage/WebSWContextManagerConnection.h:
     33
    1342022-03-26  Yusuke Suzuki  <ysuzuki@apple.com>
    235
  • trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp

    r291492 r291938  
    660660}
    661661
     662void WebSWServerConnection::focusServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier clientIdentifier, CompletionHandler<void(std::optional<ServiceWorkerClientData>&&)>&& callback)
     663{
     664    sendWithAsyncReply(Messages::WebSWClientConnection::FocusServiceWorkerClient { clientIdentifier }, WTFMove(callback));
     665}
     666
    662667} // namespace WebKit
    663668
  • trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.h

    r288416 r291938  
    8484    void fetchTaskTimedOut(WebCore::ServiceWorkerIdentifier);
    8585
     86    void focusServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
     87
    8688private:
    8789    // Implement SWServer::Connection (Messages to the client WebProcess)
  • trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cpp

    r290776 r291938  
    218218}
    219219
     220void WebSWServerToContextConnection::focus(ScriptExecutionContextIdentifier clientIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&& callback)
     221{
     222    auto* server = this->server();
     223    auto* connection = server ? server->connection(clientIdentifier.processIdentifier()) : nullptr;
     224    if (!connection) {
     225        callback({ });
     226        return;
     227    }
     228    connection->focusServiceWorkerClient(clientIdentifier, WTFMove(callback));
     229}
     230
    220231} // namespace WebKit
    221232
  • trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.h

    r290776 r291938  
    9999    void firePushEvent(WebCore::ServiceWorkerIdentifier, const std::optional<Vector<uint8_t>>&, CompletionHandler<void(bool)>&&) final;
    100100    void close() final;
     101    void focus(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
    101102
    102103    void connectionIsNoLongerNeeded() final;
  • trunk/Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.messages.in

    r291467 r291938  
    3636    MatchAll(uint64_t matchAllRequestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, struct WebCore::ServiceWorkerClientQueryOptions options);
    3737    Claim(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier) -> (std::optional<WebCore::ExceptionData> result)
     38    Focus(WebCore::ScriptExecutionContextIdentifier serviceWorkerClientIdentifier) -> (std::optional<WebCore::ServiceWorkerClientData> result)
    3839    SetScriptResource(WebCore::ServiceWorkerIdentifier identifier, URL scriptURL, WebCore::ServiceWorkerContextData::ImportedScript script)
    3940    PostMessageToServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier destination, struct WebCore::MessageWithMessagePorts message, WebCore::ServiceWorkerIdentifier source, String sourceOrigin)
  • trunk/Source/WebKit/UIProcess/API/APIUIClient.h

    r289612 r291938  
    102102    virtual void focus(WebKit::WebPageProxy*) { }
    103103    virtual void unfocus(WebKit::WebPageProxy*) { }
     104    virtual bool focusFromServiceWorker(WebKit::WebPageProxy&) { return false; }
    104105
    105106    virtual void runJavaScriptAlert(WebKit::WebPageProxy&, const WTF::String&, WebKit::WebFrameProxy*, WebKit::FrameInfoData&&, Function<void()>&& completionHandler) { completionHandler(); }
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h

    r289694 r291938  
    273273- (void)_showWebView:(WKWebView *)webView WK_API_AVAILABLE(macos(10.13.4));
    274274- (void)_focusWebView:(WKWebView *)webView WK_API_AVAILABLE(macos(10.13.4));
     275- (bool)_focusWebViewFromServiceWorker:(WKWebView *)webView WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
    275276- (void)_unfocusWebView:(WKWebView *)webView WK_API_AVAILABLE(macos(10.13.4));
    276277- (void)_webViewDidScroll:(WKWebView *)webView WK_API_AVAILABLE(macos(10.13.4));
  • trunk/Source/WebKit/UIProcess/Cocoa/UIDelegate.h

    r289694 r291938  
    117117        void focus(WebPageProxy*) final;
    118118        void unfocus(WebPageProxy*) final;
     119        bool focusFromServiceWorker(WebKit::WebPageProxy&) final;
     120
    119121        bool canRunModal() const final;
    120122        void runModal(WebPageProxy&) final;
     
    210212        bool showWebView : 1;
    211213        bool focusWebView : 1;
     214        bool focusWebViewFromServiceWorker : 1;
    212215        bool unfocusWebView : 1;
    213216        bool webViewRunModal : 1;
  • trunk/Source/WebKit/UIProcess/Cocoa/UIDelegate.mm

    r290146 r291938  
    126126    m_delegateMethods.showWebView = [delegate respondsToSelector:@selector(_showWebView:)];
    127127    m_delegateMethods.focusWebView = [delegate respondsToSelector:@selector(_focusWebView:)];
     128    m_delegateMethods.focusWebViewFromServiceWorker = [delegate respondsToSelector:@selector(_focusWebViewFromServiceWorker:)];
    128129    m_delegateMethods.unfocusWebView = [delegate respondsToSelector:@selector(_unfocusWebView:)];
    129130    m_delegateMethods.webViewRunModal = [delegate respondsToSelector:@selector(_webViewRunModal:)];
     
    832833}
    833834
     835bool UIDelegate::UIClient::focusFromServiceWorker(WebKit::WebPageProxy& proxy)
     836{
     837    bool hasImplementation = m_uiDelegate && m_uiDelegate->m_delegateMethods.focusWebViewFromServiceWorker && m_uiDelegate->m_delegate.get();
     838    if (!hasImplementation) {
     839        auto* webView = m_uiDelegate ? m_uiDelegate->m_webView.get().get() : nullptr;
     840        if (!webView || !webView.window)
     841            return false;
     842
     843#if PLATFORM(MAC)
     844        [webView.window makeKeyAndOrderFront:nil];
     845#else
     846        [webView.window makeKeyAndVisible];
     847#endif
     848        [[webView window] makeFirstResponder:webView];
     849        return true;
     850    }
     851
     852    return [(id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get() _focusWebViewFromServiceWorker:m_uiDelegate->m_webView.get().get()];
     853}
     854
    834855void UIDelegate::UIClient::focus(WebPageProxy*)
    835856{
     
    843864    if (!delegate)
    844865        return;
    845    
     866
    846867    [(id <WKUIDelegatePrivate>)delegate _focusWebView:m_uiDelegate->m_webView.get().get()];
    847868}
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r291784 r291938  
    74167416    send(Messages::WebPage::DidCancelCheckingText(requestID));
    74177417}
     7418
     7419void WebPageProxy::focusFromServiceWorker(CompletionHandler<void()>&& callback)
     7420{
     7421    if (!m_uiClient->focusFromServiceWorker(*this)) {
     7422        callback();
     7423        return;
     7424    }
     7425
     7426#if PLATFORM(COCOA)
     7427    makeFirstResponder();
     7428#endif
     7429
     7430    if (m_activityState.contains(ActivityState::IsVisible)) {
     7431        callback();
     7432        return;
     7433    }
     7434    installActivityStateChangeCompletionHandler(WTFMove(callback));
     7435}
     7436
    74187437// Other
    74197438
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r291784 r291938  
    16931693    void getLoadDecisionForIcon(const WebCore::LinkIcon&, WebKit::CallbackID);
    16941694
     1695    void focusFromServiceWorker(CompletionHandler<void()>&&);
    16951696    void setFocus(bool focused);
    16961697    void setWindowFrame(const WebCore::FloatRect&);
  • trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in

    r291393 r291938  
    4242    SetFocus(bool focused)
    4343    TakeFocus(uint8_t direction)
     44    FocusFromServiceWorker() -> ()
    4445    FocusedFrameChanged(std::optional<WebCore::FrameIdentifier> frameID)
    4546    SetRenderTreeSize(uint64_t treeSize)
  • trunk/Source/WebKit/WebProcess/Storage/WebSWClientConnection.cpp

    r291003 r291938  
    4242#include <WebCore/Document.h>
    4343#include <WebCore/DocumentLoader.h>
     44#include <WebCore/FocusController.h>
     45#include <WebCore/Frame.h>
    4446#include <WebCore/ProcessIdentifier.h>
    4547#include <WebCore/SecurityOrigin.h>
     
    302304}
    303305
     306void WebSWClientConnection::focusServiceWorkerClient(ScriptExecutionContextIdentifier clientIdentifier, CompletionHandler<void(std::optional<ServiceWorkerClientData>&&)>&& callback)
     307{
     308    auto* client = Document::allDocumentsMap().get(clientIdentifier);
     309    auto* page = client ? client->page() : nullptr;
     310    if (!page) {
     311        callback({ });
     312        return;
     313    }
     314
     315    WebPage::fromCorePage(*page).sendWithAsyncReply(Messages::WebPageProxy::FocusFromServiceWorker { }, [clientIdentifier, callback = WTFMove(callback)]() mutable {
     316        auto* client = Document::allDocumentsMap().get(clientIdentifier);
     317        auto* frame = client ? client->frame() : nullptr;
     318        auto* page = frame ? frame->page() : nullptr;
     319        if (!page) {
     320            callback({ });
     321            return;
     322        }
     323        page->focusController().setFocusedFrame(frame);
     324        callback(ServiceWorkerClientData::from(*client));
     325    });
     326}
     327
    304328} // namespace WebKit
    305329
  • trunk/Source/WebKit/WebProcess/Storage/WebSWClientConnection.h

    r291003 r291938  
    100100    void setNavigationPreloadHeaderValue(WebCore::ServiceWorkerRegistrationIdentifier, String&&, ExceptionOrVoidCallback&&) final;
    101101    void getNavigationPreloadState(WebCore::ServiceWorkerRegistrationIdentifier, ExceptionOrNavigationPreloadStateCallback&&) final;
     102    void focusServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&);
    102103
    103104    void scheduleStorageJob(const WebCore::ServiceWorkerJobData&);
  • trunk/Source/WebKit/WebProcess/Storage/WebSWClientConnection.messages.in

    r290903 r291938  
    4040
    4141    SetDocumentIsControlled(WebCore::ScriptExecutionContextIdentifier temporaryDocumentIdentifier, struct WebCore::ServiceWorkerRegistrationData data) -> (bool isSuccess)
     42
     43    FocusServiceWorkerClient(WebCore::ScriptExecutionContextIdentifier clientIdentifier) -> (std::optional<WebCore::ServiceWorkerClientData> result)
    4244}
    4345
  • trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp

    r291467 r291938  
    333333}
    334334
    335 void WebSWContextManagerConnection::claim(WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, CompletionHandler<void(ExceptionOr<void>&&)>&& callback)
     335void WebSWContextManagerConnection::claim(ServiceWorkerIdentifier serviceWorkerIdentifier, CompletionHandler<void(ExceptionOr<void>&&)>&& callback)
    336336{
    337337    m_connectionToNetworkProcess->sendWithAsyncReply(Messages::WebSWServerToContextConnection::Claim { serviceWorkerIdentifier }, [callback = WTFMove(callback)](auto&& result) mutable {
     
    340340}
    341341
     342void WebSWContextManagerConnection::focus(ScriptExecutionContextIdentifier clientIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&& callback)
     343{
     344    m_connectionToNetworkProcess->sendWithAsyncReply(Messages::WebSWServerToContextConnection::Focus { clientIdentifier }, WTFMove(callback));
     345}
     346
    342347void WebSWContextManagerConnection::close()
    343348{
  • trunk/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h

    r291467 r291938  
    8282    void matchAll(WebCore::ServiceWorkerIdentifier, const WebCore::ServiceWorkerClientQueryOptions&, WebCore::ServiceWorkerClientsMatchAllCallback&&) final;
    8383    void claim(WebCore::ServiceWorkerIdentifier, CompletionHandler<void(WebCore::ExceptionOr<void>&&)>&&) final;
     84    void focus(WebCore::ScriptExecutionContextIdentifier, CompletionHandler<void(std::optional<WebCore::ServiceWorkerClientData>&&)>&&) final;
    8485    void skipWaiting(WebCore::ServiceWorkerIdentifier, CompletionHandler<void()>&&) final;
    8586    void setScriptResource(WebCore::ServiceWorkerIdentifier, const URL&, const WebCore::ServiceWorkerContextData::ImportedScript&) final;
  • trunk/Tools/ChangeLog

    r291937 r291938  
     12022-03-26  Youenn Fablet  <youenn@apple.com>
     2
     3        Implement ServiceWorkerWindowClient.focus
     4        https://bugs.webkit.org/show_bug.cgi?id=238319
     5        <rdar://90616490>
     6
     7        Reviewed by Brady Eidson.
     8
     9        * TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:
     10
    1112022-03-26  Yusuke Suzuki  <ysuzuki@apple.com>
    212
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm

    r287223 r291938  
    4747#import <WebKit/WebKit.h>
    4848#import <WebKit/_WKExperimentalFeature.h>
     49#import <WebKit/_WKInternalDebugFeature.h>
    4950#import <WebKit/_WKProcessPoolConfiguration.h>
    5051#import <WebKit/_WKRemoteObjectInterface.h>
     
    28032804    TestWebKitAPI::Util::run(&done);
    28042805}
     2806
     2807static const char* ServiceWorkerWindowClientFocusMain =
     2808"<div>test page</div>"
     2809"<script>"
     2810"let worker;"
     2811"async function test() {"
     2812"    try {"
     2813"        const registration = await navigator.serviceWorker.register('/sw.js');"
     2814"        if (registration.active) {"
     2815"            worker = registration.active;"
     2816"            alert('already active');"
     2817"            return;"
     2818"        }"
     2819"        worker = registration.installing;"
     2820"        worker.addEventListener('statechange', () => {"
     2821"            if (worker.state == 'activated')"
     2822"                alert('successfully registered');"
     2823"        });"
     2824"    } catch(e) {"
     2825"        alert('Exception: ' + e);"
     2826"    }"
     2827"}"
     2828"window.onload = test;"
     2829""
     2830"function focusClient() {"
     2831"    worker.postMessage('start');"
     2832"    navigator.serviceWorker.onmessage = (event) => {"
     2833"        window.webkit.messageHandlers.sw.postMessage(event.data);"
     2834"    }"
     2835"}"
     2836""
     2837"function checkFocusValue(value, name) {"
     2838"    window.webkit.messageHandlers.sw.postMessage(document.hasFocus() === value ? 'PASS' : 'FAIL: expected ' + value + ' for ' + name);"
     2839"}"
     2840"</script>";
     2841static const char* ServiceWorkerWindowClientFocusJS =
     2842"self.addEventListener('message', (event) => {"
     2843"   event.source.focus().then((client) => {"
     2844"       event.source.postMessage('focused');"
     2845"   }, (error) => {"
     2846"       event.source.postMessage('not focused');"
     2847"   });"
     2848"});";
     2849
     2850TEST(ServiceWorker, ServiceWorkerWindowClientFocus)
     2851{
     2852    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
     2853
     2854    // Start with a clean slate data store
     2855    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
     2856        done = true;
     2857    }];
     2858    TestWebKitAPI::Util::run(&done);
     2859    done = false;
     2860
     2861    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     2862    auto preferences = [configuration preferences];
     2863
     2864    for (_WKInternalDebugFeature *feature in [WKPreferences _internalDebugFeatures]) {
     2865        if ([feature.key isEqualToString:@"ServiceWorkersUserGestureEnabled"])
     2866            [preferences _setEnabled:NO forInternalDebugFeature:feature];
     2867    }
     2868
     2869    auto messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
     2870    [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
     2871
     2872    auto webView1 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
     2873    auto webView2 = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
     2874
     2875    TestWebKitAPI::HTTPServer server({
     2876        { "/", { ServiceWorkerWindowClientFocusMain } },
     2877        { "/sw.js", { {{ "Content-Type", "application/javascript" }}, ServiceWorkerWindowClientFocusJS } }
     2878    });
     2879
     2880    [webView1 loadRequest:server.request()];
     2881    EXPECT_WK_STREQ([webView1 _test_waitForAlert], "successfully registered");
     2882
     2883    [webView2 loadRequest:server.request()];
     2884    EXPECT_WK_STREQ([webView2 _test_waitForAlert], "already active");
     2885
     2886#if PLATFORM(MAC)
     2887    [[webView1 hostWindow] miniaturize:[webView1 hostWindow]];
     2888    [[webView2 hostWindow] miniaturize:[webView2 hostWindow]];
     2889    EXPECT_FALSE([webView1 hostWindow].isVisible);
     2890    EXPECT_FALSE([webView2 hostWindow].isVisible);
     2891#endif
     2892
     2893    done = false;
     2894    expectedMessage = "focused";
     2895    [webView1 evaluateJavaScript:@"focusClient()" completionHandler: nil];
     2896    TestWebKitAPI::Util::run(&done);
     2897#if PLATFORM(MAC)
     2898    EXPECT_TRUE([webView1 hostWindow].isVisible);
     2899    EXPECT_FALSE([webView2 hostWindow].isVisible);
     2900    EXPECT_FALSE([webView1 hostWindow].isMiniaturized);
     2901    EXPECT_TRUE([webView2 hostWindow].isMiniaturized);
     2902
     2903    // FIXME: We should be able to run these tests in iOS once pages are actually visible.
     2904    done = false;
     2905    expectedMessage = "PASS";
     2906    [webView1 evaluateJavaScript:@"checkFocusValue(true, 'webView1')" completionHandler:nil];
     2907    TestWebKitAPI::Util::run(&done);
     2908
     2909    done = false;
     2910    expectedMessage = "PASS";
     2911    [webView2 evaluateJavaScript:@"checkFocusValue(false, 'webView2')" completionHandler:nil];
     2912    TestWebKitAPI::Util::run(&done);
     2913#endif
     2914
     2915    done = false;
     2916    expectedMessage = "focused";
     2917    [webView2 evaluateJavaScript:@"focusClient()" completionHandler: nil];
     2918    TestWebKitAPI::Util::run(&done);
     2919#if PLATFORM(MAC)
     2920    EXPECT_TRUE([webView2 hostWindow].isVisible);
     2921    EXPECT_FALSE([webView1 hostWindow].isMiniaturized);
     2922    EXPECT_FALSE([webView2 hostWindow].isMiniaturized);
     2923
     2924    // FIXME: We should be able to run these tests in iOS once pages are actually visible.
     2925    done = false;
     2926    expectedMessage = "PASS";
     2927    [webView2 evaluateJavaScript:@"checkFocusValue(true, 'webView2')" completionHandler:nil];
     2928    TestWebKitAPI::Util::run(&done);
     2929#endif
     2930}
     2931
     2932TEST(ServiceWorker, ServiceWorkerWindowClientFocusRequiresUserGesture)
     2933{
     2934    [WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
     2935
     2936    // Start with a clean slate data store
     2937    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
     2938        done = true;
     2939    }];
     2940    TestWebKitAPI::Util::run(&done);
     2941    done = false;
     2942
     2943    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     2944    auto preferences = [configuration preferences];
     2945
     2946    for (_WKInternalDebugFeature *feature in [WKPreferences _internalDebugFeatures]) {
     2947        if ([feature.key isEqualToString:@"ServiceWorkersUserGestureEnabled"])
     2948            [preferences _setEnabled:YES forInternalDebugFeature:feature];
     2949    }
     2950
     2951    auto messageHandler = adoptNS([[SWMessageHandlerWithExpectedMessage alloc] init]);
     2952    [[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];
     2953
     2954    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
     2955
     2956    TestWebKitAPI::HTTPServer server({
     2957        { "/", { ServiceWorkerWindowClientFocusMain } },
     2958        { "/sw.js", { {{ "Content-Type", "application/javascript" }}, ServiceWorkerWindowClientFocusJS } }
     2959    });
     2960
     2961    [webView loadRequest:server.request()];
     2962    EXPECT_WK_STREQ([webView _test_waitForAlert], "successfully registered");
     2963
     2964    done = false;
     2965    expectedMessage = "not focused";
     2966    [webView evaluateJavaScript:@"focusClient()" completionHandler: nil];
     2967    TestWebKitAPI::Util::run(&done);
     2968}
Note: See TracChangeset for help on using the changeset viewer.