Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Service workers in browser extensions are not able to load resources …
…over a custom protocol

https://bugs.webkit.org/show_bug.cgi?id=242871
<rdar://96973622>

Reviewed by Geoffrey Garen and Timothy Hatcher.

Loads from service workers could not get serviced by a custom scheme handler. Part of the
issue is that the custom scheme handlers as per WKWebView but service workers can be shared
by many web views. However, the service workers from browser extensions are special in that
they run on the main thread and they have a tight coupling with a WKWevView (and its
internal service worker page). In WebLoaderStrategy::tryLoadingUsingURLSchemeHandler(), we
now add support for service workers from browser extensions by using the URL scheme handlers
from their associated service worker page.

Regular / traditional service workers still have no support for custom scheme handlers and
this is something we should consider fixing in the future. However, this patch takes care
of extension service workers, which is what the radar is about since they rely on custom
scheme handlers to do loads from the extension bundle. This is an important use case we
need to support ASAP.

* Source/WebCore/page/Page.cpp:
(WebCore::Page::serviceWorkerPage):
* Source/WebCore/page/Page.h:
* Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp:
(WebCore::ServiceWorkerGlobalScope::serviceWorkerPage):
* Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp:
(WebKit::WebLoaderStrategy::tryLoadingUsingURLSchemeHandler):
* Source/WebKit/WebProcess/Storage/RemoteWorkerFrameLoaderClient.h:
* Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp:
(WebKit::WebSWContextManagerConnection::installServiceWorker):
* Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm:
(-[ServiceWorkerSchemeHandler webView:startURLSchemeTask:]):

Canonical link: https://commits.webkit.org/252585@main
  • Loading branch information
cdumez committed Jul 19, 2022
1 parent 038c4c1 commit fcb40ae
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 11 deletions.
6 changes: 6 additions & 0 deletions Source/WebCore/page/Page.cpp
Expand Up @@ -3864,6 +3864,12 @@ ImageOverlayController& Page::imageOverlayController()
return *m_imageOverlayController;
}

Page* Page::serviceWorkerPage(ScriptExecutionContextIdentifier serviceWorkerPageIdentifier)
{
auto* serviceWorkerPageDocument = Document::allDocumentsMap().get(serviceWorkerPageIdentifier);
return serviceWorkerPageDocument ? serviceWorkerPageDocument->page() : nullptr;
}

#if ENABLE(IMAGE_ANALYSIS)

ImageAnalysisQueue& Page::imageAnalysisQueue()
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/page/Page.h
Expand Up @@ -596,6 +596,8 @@ class Page : public Supplementable<Page>, public CanMakeWeakPtr<Page> {
bool isServiceWorkerPage() const { return m_isServiceWorkerPage; }
void markAsServiceWorkerPage() { m_isServiceWorkerPage = true; }

WEBCORE_EXPORT static Page* serviceWorkerPage(ScriptExecutionContextIdentifier);

#if ENABLE(SERVICE_WORKER)
// Service worker pages have an associated ServiceWorkerGlobalScope on the main thread.
void setServiceWorkerGlobalScope(ServiceWorkerGlobalScope&);
Expand Down
3 changes: 1 addition & 2 deletions Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp
Expand Up @@ -108,8 +108,7 @@ Page* ServiceWorkerGlobalScope::serviceWorkerPage()
return nullptr;

RELEASE_ASSERT(isMainThread());
auto* serviceWorkerPageDocument = Document::allDocumentsMap().get(*m_contextData.serviceWorkerPageIdentifier);
return serviceWorkerPageDocument ? serviceWorkerPageDocument->page() : nullptr;
return Page::serviceWorkerPage(*m_contextData.serviceWorkerPageIdentifier);
}

void ServiceWorkerGlobalScope::skipWaiting(Ref<DeferredPromise>&& promise)
Expand Down
24 changes: 17 additions & 7 deletions Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp
Expand Up @@ -255,13 +255,23 @@ void WebLoaderStrategy::scheduleLoad(ResourceLoader& resourceLoader, CachedResou

bool WebLoaderStrategy::tryLoadingUsingURLSchemeHandler(ResourceLoader& resourceLoader, const WebResourceLoader::TrackingParameters& trackingParameters)
{
auto* webFrameLoaderClient = toWebFrameLoaderClient(resourceLoader.frameLoader()->client());
if (!webFrameLoaderClient)
return false;
RefPtr<WebPage> webPage;
RefPtr<WebFrame> webFrame;

if (auto* webFrameLoaderClient = toWebFrameLoaderClient(resourceLoader.frameLoader()->client())) {
webFrame = &webFrameLoaderClient->webFrame();
webPage = webFrame->page();
} else if (auto* workerFrameLoaderClient = dynamicDowncast<RemoteWorkerFrameLoaderClient>(resourceLoader.frameLoader()->client())) {
if (auto serviceWorkerPageIdentifier = workerFrameLoaderClient->serviceWorkerPageIdentifier()) {
if (auto* page = Page::serviceWorkerPage(*serviceWorkerPageIdentifier)) {
webPage = &WebPage::fromCorePage(*page);
auto* frame = webPage->mainFrame();
webFrame = frame ? WebFrame::fromCoreFrame(*frame) : nullptr;
}
}
}

auto& webFrame = webFrameLoaderClient->webFrame();
auto* webPage = webFrame.page();
if (!webPage)
if (!webPage || !webFrame)
return false;

auto* handler = webPage->urlSchemeHandlerForScheme(resourceLoader.request().url().protocol());
Expand All @@ -271,7 +281,7 @@ bool WebLoaderStrategy::tryLoadingUsingURLSchemeHandler(ResourceLoader& resource
LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, URL '%s' will be handled by a UIProcess URL scheme handler.", resourceLoader.url().string().utf8().data());
WEBLOADERSTRATEGY_RELEASE_LOG("tryLoadingUsingURLSchemeHandler: URL will be handled by a UIProcess URL scheme handler");

handler->startNewTask(resourceLoader, webFrame);
handler->startNewTask(resourceLoader, *webFrame);
return true;
}

Expand Down
Expand Up @@ -27,6 +27,7 @@

#include "WebPageProxyIdentifier.h"
#include <WebCore/EmptyFrameLoaderClient.h>
#include <WebCore/ScriptExecutionContextIdentifier.h>

namespace WebKit {

Expand All @@ -38,6 +39,9 @@ class RemoteWorkerFrameLoaderClient final : public WebCore::EmptyFrameLoaderClie

void setUserAgent(String&& userAgent) { m_userAgent = WTFMove(userAgent); }

void setServiceWorkerPageIdentifier(WebCore::ScriptExecutionContextIdentifier serviceWorkerPageIdentifier) { m_serviceWorkerPageIdentifier = serviceWorkerPageIdentifier; }
std::optional<WebCore::ScriptExecutionContextIdentifier> serviceWorkerPageIdentifier() const { return m_serviceWorkerPageIdentifier; }

private:
Ref<WebCore::DocumentLoader> createDocumentLoader(const WebCore::ResourceRequest&, const WebCore::SubstituteData&) final;

Expand All @@ -53,6 +57,7 @@ class RemoteWorkerFrameLoaderClient final : public WebCore::EmptyFrameLoaderClie
WebCore::PageIdentifier m_pageID;
WebCore::FrameIdentifier m_frameID;
String m_userAgent;
std::optional<WebCore::ScriptExecutionContextIdentifier> m_serviceWorkerPageIdentifier;
};

} // namespace WebKit
Expand Down
Expand Up @@ -160,7 +160,10 @@ void WebSWContextManagerConnection::installServiceWorker(ServiceWorkerContextDat
if (effectiveUserAgent.isNull())
effectiveUserAgent = m_userAgent;

pageConfiguration.loaderClientForMainFrame = makeUniqueRef<RemoteWorkerFrameLoaderClient>(m_webPageProxyID, m_pageID, FrameIdentifier::generate(), effectiveUserAgent);
auto loaderClientForMainFrame = makeUniqueRef<RemoteWorkerFrameLoaderClient>(m_webPageProxyID, m_pageID, FrameIdentifier::generate(), effectiveUserAgent);
if (contextData.serviceWorkerPageIdentifier)
loaderClientForMainFrame->setServiceWorkerPageIdentifier(*contextData.serviceWorkerPageIdentifier);
pageConfiguration.loaderClientForMainFrame = WTFMove(loaderClientForMainFrame);

#if !RELEASE_LOG_DISABLED
auto serviceWorkerIdentifier = contextData.serviceWorkerIdentifier;
Expand Down
17 changes: 16 additions & 1 deletion Tools/TestWebKitAPI/Tests/WebKitCocoa/ServiceWorkerBasic.mm
Expand Up @@ -2551,6 +2551,8 @@ HTTPServer server({
}

static bool didStartURLSchemeTask = false;
static bool didStartURLSchemeTaskForXMLFile = false;
static bool didStartURLSchemeTaskForImportedScript = false;

@interface ServiceWorkerSchemeHandler : NSObject <WKURLSchemeHandler> {
const char* _bytes;
Expand Down Expand Up @@ -2583,6 +2585,12 @@ - (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)ta

NSURL *finalURL = task.request.URL;

if (URL(finalURL).string().endsWith("importedScript.js"_s))
didStartURLSchemeTaskForImportedScript = true;

if (URL(finalURL).string().endsWith(".xml"_s))
didStartURLSchemeTaskForXMLFile = true;

NSMutableDictionary* headerDictionary = [NSMutableDictionary dictionary];
if (URL(finalURL).string().endsWith(".js"_s))
[headerDictionary setObject:@"text/javascript" forKey:@"Content-Type"];
Expand Down Expand Up @@ -2636,7 +2644,9 @@ - (void)serviceWorkerGlobalObjectIsAvailable

auto schemeHandler = adoptNS([ServiceWorkerSchemeHandler new]);
[schemeHandler addMappingFromURLString:@"sw-ext://ABC/other.html" toData:"foo"];
[schemeHandler addMappingFromURLString:@"sw-ext://ABC/sw.js" toData:""];
[schemeHandler addMappingFromURLString:@"sw-ext://ABC/sw.js" toData:"importScripts('sw-ext://ABC/importedScript.js');"];
[schemeHandler addMappingFromURLString:@"sw-ext://ABC/bar.xml" toData:"bar"];
[schemeHandler addMappingFromURLString:@"sw-ext://ABC/importedScript.js" toData:"fetch('sw-ext://ABC/bar.xml');"];

WKWebViewConfiguration *webViewConfiguration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"ServiceWorkerPagePlugIn"];

Expand All @@ -2661,6 +2671,8 @@ - (void)serviceWorkerGlobalObjectIsAvailable
// The service worker script should get loaded over the custom scheme handler.
done = false;
didStartURLSchemeTask = false;
didStartURLSchemeTaskForXMLFile = false;
didStartURLSchemeTaskForImportedScript = false;
[webView _loadServiceWorker:[NSURL URLWithString:@"sw-ext://ABC/sw.js"] completionHandler:^(BOOL success) {
EXPECT_TRUE(success);
EXPECT_TRUE(didStartURLSchemeTask);
Expand All @@ -2686,6 +2698,9 @@ - (void)serviceWorkerGlobalObjectIsAvailable

EXPECT_WK_STREQ([webView URL].absoluteString, @"sw-ext://ABC");

TestWebKitAPI::Util::run(&didStartURLSchemeTaskForImportedScript);
TestWebKitAPI::Util::run(&didStartURLSchemeTaskForXMLFile);

// The service worker should exit if we close/deallocate the view we used to launch it.
[webView _close];
webView = nil;
Expand Down

0 comments on commit fcb40ae

Please sign in to comment.