Skip to content

Commit

Permalink
Cherry-pick 272448.87@safari-7618-branch (b70a943). https://bugs.webk…
Browse files Browse the repository at this point in the history
…it.org/show_bug.cgi?id=267298

    Regression: Third-party service worker processes get killed when trying to do loads
    https://bugs.webkit.org/show_bug.cgi?id=267298
    rdar://120587179

    Reviewed by Youenn Fablet.

    We recently added origin IPC message checks to make sure that WebProcesses can
    only access cookies from specific origins. Due to a bug in our service worker
    code, the network process would allow the wrong origin in the case of
    third-party service workers (it would allow the client origin instead of the
    top origin). As a result, the network process would kill the third-party
    service worker process as soon as it would try to attempt a load. This was
    occurring on https://boingboing.net/2024/01/06/stanley-water-bottle-madness-grips-america.html
    for example, with the tiktok.com service worker.

    When the network process needs to launch a service worker process, it sends an
    EstablishRemoteWorkerContextConnectionToNetworkProcess IPC to the UIProcess,
    with a given registrable domain. The UIProcess would use this registrable
    domain to select a suitable WebProcess or launch one. When launching a new
    process, the UIProcess would send an IPC to the network process telling it this
    new WebProcess is allowed to access cookies under the given registrable domain.

    Both from the process selection point of view and from the network process
    cookie access point of view, we expect this registrable domain to be the top
    origin's registrable domain. As a result, in the example above, the third-party
    tiktok.com service worker under boingboing.net, would be expected to use a
    "boingboing.net" WebProcess and only have access to cookies under the
    "boingboing.net" first party.

    However, our service worker logic was using the registrable domain of the
    service worker script URL instead. As a result, we would select a "tiktok.com"
    WebProcess for the service worker process, which was wrong from a site
    isolation perspective (since top-level tiktok.com would share the same process
    as tiktok.com under boingboing.net). Also, the network process would allow
    access to top-level tiktok.com cookies if the WebProcess requested them, which
    was wrong too.

    Later on, the service worker would try to do a load. The network request would
    request use "boingboing.net" as firstParty for cookies, which is correct.
    However, the network process would reject such load, since the process is only
    allowed to use "tiktok.com" as first party for cookies. It would then kill the
    service worker process for good measure since it would assume it is compromised.

    To address the issue, we now properly use the registrable domain of the top
    level origin when sending the EstablishRemoteWorkerContextConnectionToNetworkProcess
    IPC for and service worker connection selection in general. I updated the shared
    worker code as well to maintain consistency.

    Note that in order to write an API test for this, I had to restore the service
    worker from disk first. When the service worker is newly registered by JS, we
    would first tell the network process to allow the wrong client origin. However,
    a later IPC to tell the network process to also allow the top level origin. As
    a result, newly registered third-party service workers would not get terminated
    which is why our tests were passing. Those service worker origins were allowed
    access to cookies they shouldn't have access to though so there was a security
    issue still for them.
    When restoring the service worker from disk though, we'd only send a single IPC
    to the network process telling it to allow the original of the service worker
    script URL. This allowed me to write a test and explains the service worker
    processes terminations on boingboing.net.

    * Source/WebCore/workers/service/server/SWServer.cpp:
    (WebCore::SWServer::addRegistrationFromStore):
    (WebCore::SWServer::scheduleJob):
    (WebCore::SWServer::tryInstallContextData):
    (WebCore::SWServer::runServiceWorkerIfNecessary):
    (WebCore::SWServer::markAllWorkersForRegistrableDomainAsTerminated):
    (WebCore::SWServer::removeContextConnectionIfPossible):
    (WebCore::SWServer::fireFunctionalEvent):
    * Source/WebCore/workers/service/server/SWServerToContextConnection.cpp:
    (WebCore::SWServerToContextConnection::terminateWhenPossible):
    * Source/WebCore/workers/service/server/SWServerWorker.cpp:
    (WebCore::m_lastNavigationWasAppInitiated):
    (WebCore::SWServerWorker::contextConnection):
    (WebCore::SWServerWorker::terminationIfPossibleTimerFired):
    * Source/WebCore/workers/service/server/SWServerWorker.h:
    (WebCore::SWServerWorker::topRegistrableDomain const):
    (WebCore::SWServerWorker::registrableDomain const): Deleted.
    * Source/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cpp:
    (WebKit::WebSWServerConnection::startFetch):
    * Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:
    (-[SWMessageHandlerForRestoreFromDiskTest resetExpectedMessage:]):

    Canonical link: https://commits.webkit.org/272448.87@safari-7618-branch

Canonical link: https://commits.webkit.org/266719.389@webkitglib/2.42
  • Loading branch information
cdumez authored and aperezdc committed Mar 14, 2024
1 parent 0cc7e52 commit f6b9e4a
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 24 deletions.
16 changes: 8 additions & 8 deletions Source/WebCore/workers/service/server/SWServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data, Complet

LOG(ServiceWorker, "Adding registration from store for %s", data.registration.key.loggingString().utf8().data());

auto registrableDomain = WebCore::RegistrableDomain(data.scriptURL);
auto registrableDomain = WebCore::RegistrableDomain(data.registration.key.topOrigin());
validateRegistrationDomain(registrableDomain, ServiceWorkerJobType::Register, m_scopeToRegistrationMap.contains(data.registration.key), [this, weakThis = WeakPtr { *this }, data = WTFMove(data), completionHandler = WTFMove(completionHandler)] (bool isValid) mutable {
ASSERT(isMainThread());
if (!weakThis)
Expand Down Expand Up @@ -465,7 +465,7 @@ void SWServer::scheduleJob(ServiceWorkerJobData&& jobData)
{
ASSERT(m_connections.contains(jobData.connectionIdentifier()) || jobData.connectionIdentifier() == Process::identifier());

validateRegistrationDomain(WebCore::RegistrableDomain(jobData.scriptURL), jobData.type, m_scopeToRegistrationMap.contains(jobData.registrationKey()), [this, weakThis = WeakPtr { *this }, jobData = WTFMove(jobData)] (bool isValid) mutable {
validateRegistrationDomain(WebCore::RegistrableDomain(jobData.topOrigin), jobData.type, m_scopeToRegistrationMap.contains(jobData.registrationKey()), [this, weakThis = WeakPtr { *this }, jobData = WTFMove(jobData)] (bool isValid) mutable {
if (!weakThis)
return;
if (m_hasServiceWorkerEntitlement || isValid) {
Expand Down Expand Up @@ -843,7 +843,7 @@ LastNavigationWasAppInitiated SWServer::clientIsAppInitiatedForRegistrableDomain

void SWServer::tryInstallContextData(const std::optional<ProcessIdentifier>& requestingProcessIdentifier, ServiceWorkerContextData&& data)
{
RegistrableDomain registrableDomain(data.scriptURL);
RegistrableDomain registrableDomain(data.registration.key.topOrigin());
auto* connection = contextConnectionForRegistrableDomain(registrableDomain);
if (!connection) {
auto firstPartyForCookies = data.registration.key.firstPartyForCookies();
Expand Down Expand Up @@ -958,14 +958,14 @@ void SWServer::runServiceWorkerIfNecessary(SWServerWorker& worker, RunServiceWor
}

if (!contextConnection) {
auto& serviceWorkerRunRequestsForOrigin = m_serviceWorkerRunRequests.ensure(worker.registrableDomain(), [] {
auto& serviceWorkerRunRequestsForOrigin = m_serviceWorkerRunRequests.ensure(worker.topRegistrableDomain(), [] {
return HashMap<ServiceWorkerIdentifier, Vector<RunServiceWorkerCallback>> { };
}).iterator->value;
serviceWorkerRunRequestsForOrigin.ensure(worker.identifier(), [&] {
return Vector<RunServiceWorkerCallback> { };
}).iterator->value.append(WTFMove(callback));

createContextConnection(worker.registrableDomain(), worker.serviceWorkerPageIdentifier());
createContextConnection(worker.topRegistrableDomain(), worker.serviceWorkerPageIdentifier());
return;
}

Expand Down Expand Up @@ -1006,7 +1006,7 @@ void SWServer::markAllWorkersForRegistrableDomainAsTerminated(const RegistrableD
{
Vector<SWServerWorker*> terminatedWorkers;
for (auto& worker : m_runningOrTerminatingWorkers.values()) {
if (worker->registrableDomain() == registrableDomain)
if (worker->topRegistrableDomain() == registrableDomain)
terminatedWorkers.append(worker.ptr());
}
for (auto& terminatedWorker : terminatedWorkers)
Expand Down Expand Up @@ -1283,7 +1283,7 @@ SWServer::ShouldDelayRemoval SWServer::removeContextConnectionIfPossible(const R
return ShouldDelayRemoval::No;

for (auto& worker : m_runningOrTerminatingWorkers.values()) {
if (worker->isRunning() && worker->registrableDomain() == domain && worker->shouldContinue())
if (worker->isRunning() && worker->topRegistrableDomain() == domain && worker->shouldContinue())
return ShouldDelayRemoval::Yes;
}

Expand Down Expand Up @@ -1682,7 +1682,7 @@ void SWServer::fireFunctionalEvent(SWServerRegistration& registration, Completio
}

if (!worker->contextConnection())
createContextConnection(worker->registrableDomain(), worker->serviceWorkerPageIdentifier());
createContextConnection(worker->topRegistrableDomain(), worker->serviceWorkerPageIdentifier());

runServiceWorkerIfNecessary(serviceWorkerIdentifier, [callback = WTFMove(callback)](auto* contextConnection) mutable {
if (!contextConnection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void SWServerToContextConnection::terminateWhenPossible()

bool hasServiceWorkerWithPendingEvents = false;
server()->forEachServiceWorker([&](auto& worker) {
if (worker.isRunning() && worker.registrableDomain() == m_registrableDomain && worker.hasPendingEvents()) {
if (worker.isRunning() && worker.topRegistrableDomain() == m_registrableDomain && worker.hasPendingEvents()) {
hasServiceWorkerWithPendingEvents = true;
return false;
}
Expand Down
8 changes: 4 additions & 4 deletions Source/WebCore/workers/service/server/SWServerWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ SWServerWorker::SWServerWorker(SWServer& server, SWServerRegistration& registrat
, m_contentSecurityPolicy(contentSecurityPolicy)
, m_crossOriginEmbedderPolicy(crossOriginEmbedderPolicy)
, m_referrerPolicy(WTFMove(referrerPolicy))
, m_registrableDomain(m_data.scriptURL)
, m_topRegistrableDomain(m_registrationKey.topOrigin())
, m_scriptResourceMap(WTFMove(scriptResourceMap))
, m_terminationTimer(*this, &SWServerWorker::terminationTimerFired)
, m_terminationIfPossibleTimer(*this, &SWServerWorker::terminationIfPossibleTimerFired)
, m_lastNavigationWasAppInitiated(m_server->clientIsAppInitiatedForRegistrableDomain(m_registrableDomain))
, m_lastNavigationWasAppInitiated(m_server->clientIsAppInitiatedForRegistrableDomain(m_topRegistrableDomain))
{
m_data.scriptURL.removeFragmentIdentifier();

Expand Down Expand Up @@ -176,7 +176,7 @@ const ClientOrigin& SWServerWorker::origin() const

SWServerToContextConnection* SWServerWorker::contextConnection()
{
return m_server ? m_server->contextConnectionForRegistrableDomain(registrableDomain()) : nullptr;
return m_server ? m_server->contextConnectionForRegistrableDomain(topRegistrableDomain()) : nullptr;
}

void SWServerWorker::scriptContextFailedToStart(const std::optional<ServiceWorkerJobDataIdentifier>& jobDataIdentifier, const String& message)
Expand Down Expand Up @@ -435,7 +435,7 @@ void SWServerWorker::terminationIfPossibleTimerFired()
return;

terminate();
m_server->removeContextConnectionIfPossible(registrableDomain());
m_server->removeContextConnectionIfPossible(topRegistrableDomain());
}

bool SWServerWorker::isClientActiveServiceWorker(ScriptExecutionContextIdentifier clientIdentifier) const
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/workers/service/server/SWServerWorker.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class SWServerWorker : public RefCounted<SWServerWorker> {
ServiceWorkerContextData contextData() const;

WEBCORE_EXPORT const ClientOrigin& origin() const;
const RegistrableDomain& registrableDomain() const { return m_registrableDomain; }
const RegistrableDomain& topRegistrableDomain() const { return m_topRegistrableDomain; }
WEBCORE_EXPORT std::optional<ScriptExecutionContextIdentifier> serviceWorkerPageIdentifier() const;

WEBCORE_EXPORT SWServerToContextConnection* contextConnection();
Expand Down Expand Up @@ -176,7 +176,7 @@ class SWServerWorker : public RefCounted<SWServerWorker> {
bool m_hasPendingEvents { false };
State m_state { State::NotRunning };
mutable std::optional<ClientOrigin> m_origin;
RegistrableDomain m_registrableDomain;
RegistrableDomain m_topRegistrableDomain;
bool m_isSkipWaitingFlagSet { false };
Vector<CompletionHandler<void(bool)>> m_whenActivatedHandlers;
MemoryCompactRobinHoodHashMap<URL, ServiceWorkerContextData::ImportedScript> m_scriptResourceMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ void WebSWServerConnection::startFetch(ServiceWorkerFetchTask& task, SWServerWor
}

if (!worker->contextConnection())
server().createContextConnection(worker->registrableDomain(), worker->serviceWorkerPageIdentifier());
server().createContextConnection(worker->topRegistrableDomain(), worker->serviceWorkerPageIdentifier());

auto identifier = task->serviceWorkerIdentifier();
server().runServiceWorkerIfNecessary(identifier, [weakThis = WTFMove(weakThis), this, task = WTFMove(task)](auto* contextConnection) mutable {
Expand Down
6 changes: 3 additions & 3 deletions Source/WebKit/NetworkProcess/SharedWorker/WebSharedWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ WebSharedWorker* WebSharedWorker::fromIdentifier(WebCore::SharedWorkerIdentifier
return allWorkers().get(identifier);
}

WebCore::RegistrableDomain WebSharedWorker::registrableDomain() const
WebCore::RegistrableDomain WebSharedWorker::topRegistrableDomain() const
{
return WebCore::RegistrableDomain { url() };
return WebCore::RegistrableDomain { m_key.origin.topOrigin };
}

void WebSharedWorker::setFetchResult(WebCore::WorkerFetchResult&& fetchResult)
Expand Down Expand Up @@ -172,7 +172,7 @@ std::optional<WebCore::ProcessIdentifier> WebSharedWorker::firstSharedWorkerObje

WebSharedWorkerServerToContextConnection* WebSharedWorker::contextConnection() const
{
return m_server.contextConnectionForRegistrableDomain(registrableDomain());
return m_server.contextConnectionForRegistrableDomain(topRegistrableDomain());
}

} // namespace WebKit
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class WebSharedWorker : public CanMakeWeakPtr<WebSharedWorker> {
const WebCore::WorkerOptions& workerOptions() const { return m_workerOptions; }
const WebCore::ClientOrigin& origin() const { return m_key.origin; }
const URL& url() const { return m_key.url; }
WebCore::RegistrableDomain registrableDomain() const;
WebCore::RegistrableDomain topRegistrableDomain() const;
WebSharedWorkerServerToContextConnection* contextConnection() const;

void addSharedWorkerObject(WebCore::SharedWorkerObjectIdentifier, const WebCore::TransferredMessagePort&);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,16 @@ void WebSharedWorkerServer::didFinishFetchingSharedWorkerScript(WebSharedWorker&
sharedWorker.setInitializationData(WTFMove(initializationData));
sharedWorker.setFetchResult(WTFMove(fetchResult));

if (auto* connection = m_contextConnections.get(sharedWorker.registrableDomain()))
if (auto* connection = m_contextConnections.get(sharedWorker.topRegistrableDomain()))
sharedWorker.launch(*connection);
else
createContextConnection(sharedWorker.registrableDomain(), sharedWorker.firstSharedWorkerObjectProcess());
createContextConnection(sharedWorker.topRegistrableDomain(), sharedWorker.firstSharedWorkerObjectProcess());
}

bool WebSharedWorkerServer::needsContextConnectionForRegistrableDomain(const WebCore::RegistrableDomain& registrableDomain) const
{
for (auto& sharedWorker : m_sharedWorkers.values()) {
if (registrableDomain.matches(sharedWorker->url()))
if (registrableDomain == sharedWorker->topRegistrableDomain())
return true;
}
return false;
Expand Down Expand Up @@ -197,7 +197,7 @@ void WebSharedWorkerServer::contextConnectionCreated(WebSharedWorkerServerToCont
RELEASE_LOG(SharedWorker, "WebSharedWorkerServer::contextConnectionCreated(%p) webProcessIdentifier=%" PRIu64, &contextConnection, contextConnection.webProcessIdentifier().toUInt64());
auto& registrableDomain = contextConnection.registrableDomain();
for (auto& sharedWorker : m_sharedWorkers.values()) {
if (!registrableDomain.matches(sharedWorker->url()))
if (registrableDomain != sharedWorker->topRegistrableDomain())
continue;

sharedWorker->didCreateContextConnection(contextConnection);
Expand Down
164 changes: 164 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ @interface SWMessageHandlerForRestoreFromDiskTest : NSObject <WKScriptMessageHan
NSString *_expectedMessage;
}
- (instancetype)initWithExpectedMessage:(NSString *)expectedMessage;
- (void)resetExpectedMessage:(NSString *)expectedMessage;
@end

@interface SWMessageHandlerWithExpectedMessage : NSObject <WKScriptMessageHandler>
Expand All @@ -123,6 +124,11 @@ - (instancetype)initWithExpectedMessage:(NSString *)expectedMessage
return self;
}

- (void)resetExpectedMessage:(NSString *)expectedMessage
{
_expectedMessage = expectedMessage;
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
EXPECT_WK_STREQ(message.body, _expectedMessage);
Expand Down Expand Up @@ -651,6 +657,164 @@ - (void)userContentController:(WKUserContentController *)userContentController d
done = false;
}

static constexpr auto scriptBytesWithFetchSupport = R"SWRESOURCE(

self.addEventListener("message", (event) => {
if (event.data = 'do-fetch') {
fetch("foo.txt").then((response) => {
event.source.postMessage("Load succeeded");
}).catch((err) => {
event.source.postMessage("Load failed");
});
}
});

)SWRESOURCE"_s;

static constexpr auto mainRegisteringAlreadyExistingWorkerRequestFetchBytes = R"SWRESOURCE(
<script>
let activeServiceWorker = null;
try {
function log(msg)
{
window.webkit.messageHandlers.sw.postMessage(msg);
}

addEventListener("message", function(event) {
if (event.data === "do-fetch") {
if (activeServiceWorker)
activeServiceWorker.postMessage("do-fetch");
else
log("FAIL: activeServiceWorker is null");
} else
log("FAIL: unrecognized command: " + event.data);
});

navigator.serviceWorker.addEventListener("message", function(event) {
log("Message from worker: " + event.data);
});

navigator.serviceWorker.register('/sw.js').then(function(reg) {
if (reg.installing) {
log("FAIL: Registration had an installing worker");
return;
}
if (reg.active) {
if (reg.active.state == "activated") {
log("PASS: Registration already has an active worker");
activeServiceWorker = reg.active;
} else
log("FAIL: Registration has an active worker but its state is not activated");
} else
log("FAIL: Registration does not have an active worker");
}).catch(function(error) {
log("Registration failed with: " + error);
});
} catch(e) {
log("Exception: " + e);
}
</script>
)SWRESOURCE"_s;

TEST(ServiceWorkers, ThirdPartyRestoredFromDisk)
{
[WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];

TestWebKitAPI::HTTPServer server({
{ "/index.html"_s, { "<script>onload = () => { webkit.messageHandlers.sw.postMessage('LOADED'); }</script>"_s } },
{ "/thirdPartyIframeWithSW.html"_s, { mainRegisteringWorkerBytes } },
{ "/thirdPartyIframeWithSW2.html"_s, { mainRegisteringAlreadyExistingWorkerRequestFetchBytes } },
{ "/sw.js"_s, { { { "Content-Type"_s, "application/javascript"_s } }, scriptBytesWithFetchSupport } },
{ "/foo.txt"_s, { "FOO"_s } }
});

// Normally, service workers get terminated several seconds after their clients are gone.
// Disable this delay for the purpose of testing.
auto dataStoreConfiguration = adoptNS([[_WKWebsiteDataStoreConfiguration alloc] init]);
[dataStoreConfiguration setServiceWorkerProcessTerminationDelayEnabled:NO];

auto dataStore = adoptNS([[WKWebsiteDataStore alloc] _initWithConfiguration:dataStoreConfiguration.get()]);

// Start with a clean slate data store
[dataStore removeDataOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] modifiedSince:[NSDate distantPast] completionHandler:^() {
done = true;
}];
TestWebKitAPI::Util::run(&done);
done = false;

auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get().websiteDataStore = dataStore.get();

RetainPtr<SWMessageHandlerForRestoreFromDiskTest> messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"LOADED"]);
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];

RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);

[webView loadRequest:server.request("/index.html"_s)];
TestWebKitAPI::Util::run(&done);

done = false;
[messageHandler resetExpectedMessage:@"PASS: Registration was successful and service worker was activated"];

String thirdPartyIframeURL = URL(server.requestWithLocalhost("/thirdPartyIframeWithSW.html"_s).URL).string();
String injectFrameScript = makeString("let frame = document.createElement('iframe'); frame.src = '", thirdPartyIframeURL, "'; document.body.append(frame);");
bool addedIframe = false;
[webView evaluateJavaScript:(NSString *)injectFrameScript completionHandler: [&] (id, NSError *error) {
EXPECT_TRUE(!error);
addedIframe = true;
}];

TestWebKitAPI::Util::run(&addedIframe);
TestWebKitAPI::Util::run(&done);

[webView _close];
webView = nullptr;
configuration = nullptr;
messageHandler = nullptr;
done = false;

// Let the service worker process exit.
TestWebKitAPI::Util::runFor(1_s);

configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
configuration.get().websiteDataStore = dataStore.get();

messageHandler = adoptNS([[SWMessageHandlerForRestoreFromDiskTest alloc] initWithExpectedMessage:@"LOADED"]);
[[configuration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sw"];

webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);

[webView loadRequest:server.request("/index.html"_s)];
TestWebKitAPI::Util::run(&done);

done = false;
[messageHandler resetExpectedMessage:@"PASS: Registration already has an active worker"];

String thirdPartyIframeURL2 = URL(server.requestWithLocalhost("/thirdPartyIframeWithSW2.html"_s).URL).string();
String injectFrameScript2 = makeString("let frame = document.createElement('iframe'); frame.src = '", thirdPartyIframeURL2, "'; document.body.append(frame);");
addedIframe = false;
[webView evaluateJavaScript:(NSString *)injectFrameScript2 completionHandler: [&] (id, NSError *error) {
EXPECT_TRUE(!error);
addedIframe = true;
}];

TestWebKitAPI::Util::run(&addedIframe);
TestWebKitAPI::Util::run(&done);
done = false;

[messageHandler resetExpectedMessage:@"Message from worker: Load succeeded"];

bool requestedFetch = false;
[webView evaluateJavaScript:@"frames[0].postMessage('do-fetch', '*');" completionHandler: [&] (id, NSError *error) {
EXPECT_TRUE(!error);
requestedFetch = true;
}];
TestWebKitAPI::Util::run(&requestedFetch);

TestWebKitAPI::Util::run(&done);
done = false;
}

TEST(ServiceWorkers, CacheStorageRestoreFromDisk)
{
[WKWebsiteDataStore _allowWebsiteDataRecordsForAllOrigins];
Expand Down

0 comments on commit f6b9e4a

Please sign in to comment.