WKURLSchemeHandler doesn't handle sync XHR.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Jun 2018 00:34:48 +0000 (00:34 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 23 Jun 2018 00:34:48 +0000 (00:34 +0000)
<rdar://problem/40955884> and https://bugs.webkit.org/show_bug.cgi?id=186902

Reviewed by Chris Dumez.

Source/WebCore:

* English.lproj/Localizable.strings:

Source/WebKit:

This patch allows WebProcesses to block on sync loads to a custom scheme,
and teaches WebURLSchemeTasks how to buffer up data and the response if
operating synchronously.

* Shared/WebErrors.cpp:
(WebKit::failedCustomProtocolSyncLoad):
* Shared/WebErrors.h:

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::startURLSchemeTask):
(WebKit::WebPageProxy::loadSynchronousURLSchemeTask):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:

* UIProcess/WebURLSchemeHandler.cpp:
(WebKit::WebURLSchemeHandler::startTask):
* UIProcess/WebURLSchemeHandler.h:

* UIProcess/WebURLSchemeTask.cpp:
(WebKit::WebURLSchemeTask::create):
(WebKit::WebURLSchemeTask::WebURLSchemeTask):
(WebKit::WebURLSchemeTask::didPerformRedirection):
(WebKit::WebURLSchemeTask::didReceiveResponse):
(WebKit::WebURLSchemeTask::didReceiveData):
(WebKit::WebURLSchemeTask::didComplete):
(WebKit::WebURLSchemeTask::pageDestroyed):
(WebKit::WebURLSchemeTask::stop):
* UIProcess/WebURLSchemeTask.h:
(WebKit::WebURLSchemeTask::isSync const):

* WebProcess/Network/WebLoaderStrategy.cpp:
(WebKit::WebLoaderStrategy::tryLoadingSynchronouslyUsingURLSchemeHandler):
(WebKit::WebLoaderStrategy::loadResourceSynchronously):
* WebProcess/Network/WebLoaderStrategy.h:

* WebProcess/WebPage/WebURLSchemeHandlerProxy.cpp:
(WebKit::WebURLSchemeHandlerProxy::loadSynchronously):
* WebProcess/WebPage/WebURLSchemeHandlerProxy.h:

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/WKURLSchemeHandler-1.mm:
(-[SyncScheme webView:startURLSchemeTask:]):
(-[SyncScheme webView:stopURLSchemeTask:]):
(-[SyncMessageHandler userContentController:didReceiveScriptMessage:]):
(catch):

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

19 files changed:
Source/WebCore/ChangeLog
Source/WebCore/English.lproj/Localizable.strings
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp
Source/WebKit/Shared/WebErrors.cpp
Source/WebKit/Shared/WebErrors.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebPageProxy.messages.in
Source/WebKit/UIProcess/WebURLSchemeHandler.cpp
Source/WebKit/UIProcess/WebURLSchemeHandler.h
Source/WebKit/UIProcess/WebURLSchemeTask.cpp
Source/WebKit/UIProcess/WebURLSchemeTask.h
Source/WebKit/WebProcess/Network/WebLoaderStrategy.cpp
Source/WebKit/WebProcess/Network/WebLoaderStrategy.h
Source/WebKit/WebProcess/WebPage/WebURLSchemeHandlerProxy.cpp
Source/WebKit/WebProcess/WebPage/WebURLSchemeHandlerProxy.h
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/WKURLSchemeHandler-1.mm

index a976634..8b6aeed 100644 (file)
@@ -1,3 +1,12 @@
+2018-06-22  Brady Eidson  <beidson@apple.com>
+
+        WKURLSchemeHandler doesn't handle sync XHR.
+        <rdar://problem/40955884> and https://bugs.webkit.org/show_bug.cgi?id=186902
+
+        Reviewed by Chris Dumez.
+
+        * English.lproj/Localizable.strings:
+
 2018-06-22  Jer Noble  <jer.noble@apple.com>
 
         [Fullscreen] Restore ASSERT_NOT_REACHED() checks in exit fullscreen handler after r231924
index 4b79ba9..02eda19 100644 (file)
 /* Validation message for input form controls of type 'email' that have an invalid value */
 "Enter an email address" = "Enter an email address";
 
+/* Custom protocol synchronous load failure description */
+"Error handling synchronous load with custom protocol" = "Error handling synchronous load with custom protocol";
+
 /* Button for exiting full screen when in full screen media playback */
 "Exit Full Screen" = "Exit Full Screen";
 
index 8099f29..bf4c382 100644 (file)
@@ -1,3 +1,49 @@
+2018-06-22  Brady Eidson  <beidson@apple.com>
+
+        WKURLSchemeHandler doesn't handle sync XHR.
+        <rdar://problem/40955884> and https://bugs.webkit.org/show_bug.cgi?id=186902
+
+        Reviewed by Chris Dumez.
+
+        This patch allows WebProcesses to block on sync loads to a custom scheme,
+        and teaches WebURLSchemeTasks how to buffer up data and the response if 
+        operating synchronously.
+
+        * Shared/WebErrors.cpp:
+        (WebKit::failedCustomProtocolSyncLoad):
+        * Shared/WebErrors.h:
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::startURLSchemeTask):
+        (WebKit::WebPageProxy::loadSynchronousURLSchemeTask):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+
+        * UIProcess/WebURLSchemeHandler.cpp:
+        (WebKit::WebURLSchemeHandler::startTask):
+        * UIProcess/WebURLSchemeHandler.h:
+
+        * UIProcess/WebURLSchemeTask.cpp:
+        (WebKit::WebURLSchemeTask::create):
+        (WebKit::WebURLSchemeTask::WebURLSchemeTask):
+        (WebKit::WebURLSchemeTask::didPerformRedirection):
+        (WebKit::WebURLSchemeTask::didReceiveResponse):
+        (WebKit::WebURLSchemeTask::didReceiveData):
+        (WebKit::WebURLSchemeTask::didComplete):
+        (WebKit::WebURLSchemeTask::pageDestroyed):
+        (WebKit::WebURLSchemeTask::stop):
+        * UIProcess/WebURLSchemeTask.h:
+        (WebKit::WebURLSchemeTask::isSync const):
+
+        * WebProcess/Network/WebLoaderStrategy.cpp:
+        (WebKit::WebLoaderStrategy::tryLoadingSynchronouslyUsingURLSchemeHandler):
+        (WebKit::WebLoaderStrategy::loadResourceSynchronously):
+        * WebProcess/Network/WebLoaderStrategy.h:
+
+        * WebProcess/WebPage/WebURLSchemeHandlerProxy.cpp:
+        (WebKit::WebURLSchemeHandlerProxy::loadSynchronously):
+        * WebProcess/WebPage/WebURLSchemeHandlerProxy.h:
+
 2018-06-22  Chris Dumez  <cdumez@apple.com>
 
         Implement IPC throttling to keep the main thread responsive when a process misbehaves
index 4d51ba7..489354e 100644 (file)
@@ -991,7 +991,7 @@ static void logBlockedCookieInformation(const String& label, const void* loggedO
     LOCAL_LOG(R"(  "isSameSite": "%{public}s",)", sameSiteInfo.isSameSite ? "true" : "false");
     LOCAL_LOG(R"(  "isTopSite": "%{public}s",)", sameSiteInfo.isTopSite ? "true" : "false");
     LOCAL_LOG(R"(  "cookies": [])");
-    LOCAL_LOG(R"(  "})");
+    LOCAL_LOG(R"(  })");
 #undef LOCAL_LOG
 #undef LOCAL_LOG_IF_ALLOWED
 }
index 8f15cd3..b6dc50c 100644 (file)
@@ -57,6 +57,11 @@ ResourceError interruptedForPolicyChangeError(const ResourceRequest& request)
     return ResourceError(API::Error::webKitPolicyErrorDomain(), API::Error::Policy::FrameLoadInterruptedByPolicyChange, request.url(), WEB_UI_STRING("Frame load interrupted", "WebKitErrorFrameLoadInterruptedByPolicyChange description"));
 }
 
+ResourceError failedCustomProtocolSyncLoad(const ResourceRequest& request)
+{
+    return ResourceError(errorDomainWebKitInternal, 0, request.url(), WEB_UI_STRING("Error handling synchronous load with custom protocol", "Custom protocol synchronous load failure description"));
+}
+
 #if ENABLE(CONTENT_FILTERING)
 ResourceError blockedByContentFilterError(const ResourceRequest& request)
 {
index 593ea88..27d14e3 100644 (file)
@@ -41,6 +41,7 @@ WebCore::ResourceError blockedError(const WebCore::ResourceRequest&);
 WebCore::ResourceError blockedByContentBlockerError(const WebCore::ResourceRequest&);
 WebCore::ResourceError cannotShowURLError(const WebCore::ResourceRequest&);
 WebCore::ResourceError interruptedForPolicyChangeError(const WebCore::ResourceRequest&);
+WebCore::ResourceError failedCustomProtocolSyncLoad(const WebCore::ResourceRequest&);
 #if ENABLE(CONTENT_FILTERING)
 WebCore::ResourceError blockedByContentFilterError(const WebCore::ResourceRequest&);
 #endif
index 5f1521f..8563912 100644 (file)
@@ -7586,7 +7586,7 @@ void WebPageProxy::startURLSchemeTask(URLSchemeTaskParameters&& parameters)
     auto iterator = m_urlSchemeHandlersByIdentifier.find(parameters.handlerIdentifier);
     MESSAGE_CHECK(iterator != m_urlSchemeHandlersByIdentifier.end());
 
-    iterator->value->startTask(*this, parameters.taskIdentifier, WTFMove(parameters.request));
+    iterator->value->startTask(*this, parameters.taskIdentifier, WTFMove(parameters.request), nullptr);
 }
 
 void WebPageProxy::stopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskIdentifier)
@@ -7597,6 +7597,14 @@ void WebPageProxy::stopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskId
     iterator->value->stopTask(*this, taskIdentifier);
 }
 
+void WebPageProxy::loadSynchronousURLSchemeTask(URLSchemeTaskParameters&& parameters, Messages::WebPageProxy::LoadSynchronousURLSchemeTask::DelayedReply&& reply)
+{
+    auto iterator = m_urlSchemeHandlersByIdentifier.find(parameters.handlerIdentifier);
+    MESSAGE_CHECK(iterator != m_urlSchemeHandlersByIdentifier.end());
+
+    iterator->value->startTask(*this, parameters.taskIdentifier, WTFMove(parameters.request), WTFMove(reply));
+}
+
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
 void WebPageProxy::hasStorageAccess(String&& subFrameHost, String&& topFrameHost, uint64_t frameID, uint64_t webProcessContextId)
 {
index 49b21c5..2b820f2 100644 (file)
@@ -1754,6 +1754,7 @@ private:
 
     void startURLSchemeTask(URLSchemeTaskParameters&&);
     void stopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskIdentifier);
+    void loadSynchronousURLSchemeTask(URLSchemeTaskParameters&&, Messages::WebPageProxy::LoadSynchronousURLSchemeTask::DelayedReply&&);
 
     void handleAutoFillButtonClick(const UserData&);
 
index 7f3c668..4e1de5c 100644 (file)
@@ -513,6 +513,7 @@ messages -> WebPageProxy {
 
     StartURLSchemeTask(struct WebKit::URLSchemeTaskParameters parameters)
     StopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskIdentifier)
+    LoadSynchronousURLSchemeTask(struct WebKit::URLSchemeTaskParameters parameters) -> (WebCore::ResourceResponse response, WebCore::ResourceError error, IPC::DataReference data) Delayed
 
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
     HasStorageAccess(String subFrameHost, String topFrameHost, uint64_t frameID, uint64_t contextID)
index 1f682c1..dab593f 100644 (file)
@@ -49,9 +49,9 @@ WebURLSchemeHandler::~WebURLSchemeHandler()
     ASSERT(m_tasks.isEmpty());
 }
 
-void WebURLSchemeHandler::startTask(WebPageProxy& page, uint64_t taskIdentifier, ResourceRequest&& request)
+void WebURLSchemeHandler::startTask(WebPageProxy& page, uint64_t taskIdentifier, ResourceRequest&& request, SyncLoadCompletionHandler&& completionHandler)
 {
-    auto result = m_tasks.add(taskIdentifier, WebURLSchemeTask::create(*this, page, taskIdentifier, WTFMove(request)));
+    auto result = m_tasks.add(taskIdentifier, WebURLSchemeTask::create(*this, page, taskIdentifier, WTFMove(request), WTFMove(completionHandler)));
     ASSERT(result.isNewEntry);
 
     auto pageEntry = m_tasksByPageIdentifier.add(page.pageID(), HashSet<uint64_t>());
index 41f803e..b784a53 100644 (file)
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 
+namespace IPC {
+class DataReference;
+}
+
 namespace WebCore {
 class ResourceRequest;
 }
@@ -39,6 +43,8 @@ namespace WebKit {
 
 class WebPageProxy;
 
+using SyncLoadCompletionHandler = CompletionHandler<void(const WebCore::ResourceResponse&, const WebCore::ResourceError&, const IPC::DataReference&)>;
+
 class WebURLSchemeHandler : public RefCounted<WebURLSchemeHandler> {
     WTF_MAKE_NONCOPYABLE(WebURLSchemeHandler);
 public:
@@ -46,7 +52,7 @@ public:
 
     uint64_t identifier() const { return m_identifier; }
 
-    void startTask(WebPageProxy&, uint64_t taskIdentifier, WebCore::ResourceRequest&&);
+    void startTask(WebPageProxy&, uint64_t taskIdentifier, WebCore::ResourceRequest&&, SyncLoadCompletionHandler&&);
     void stopTask(WebPageProxy&, uint64_t taskIdentifier);
     void stopAllTasksForPage(WebPageProxy&);
     void taskCompleted(WebURLSchemeTask&);
@@ -65,6 +71,8 @@ private:
 
     HashMap<uint64_t, Ref<WebURLSchemeTask>> m_tasks;
     HashMap<uint64_t, HashSet<uint64_t>> m_tasksByPageIdentifier;
+    
+    SyncLoadCompletionHandler m_syncLoadCompletionHandler;
 
 }; // class WebURLSchemeHandler
 
index 5549488..7b247cc 100644 (file)
@@ -27,6 +27,7 @@
 #include "WebURLSchemeTask.h"
 
 #include "DataReference.h"
+#include "WebErrors.h"
 #include "WebPageMessages.h"
 #include "WebPageProxy.h"
 #include "WebURLSchemeHandler.h"
@@ -35,17 +36,18 @@ using namespace WebCore;
 
 namespace WebKit {
 
-Ref<WebURLSchemeTask> WebURLSchemeTask::create(WebURLSchemeHandler& handler, WebPageProxy& page, uint64_t resourceIdentifier, ResourceRequest&& request)
+Ref<WebURLSchemeTask> WebURLSchemeTask::create(WebURLSchemeHandler& handler, WebPageProxy& page, uint64_t resourceIdentifier, ResourceRequest&& request, SyncLoadCompletionHandler&& syncCompletionHandler)
 {
-    return adoptRef(*new WebURLSchemeTask(handler, page, resourceIdentifier, WTFMove(request)));
+    return adoptRef(*new WebURLSchemeTask(handler, page, resourceIdentifier, WTFMove(request), WTFMove(syncCompletionHandler)));
 }
 
-WebURLSchemeTask::WebURLSchemeTask(WebURLSchemeHandler& handler, WebPageProxy& page, uint64_t resourceIdentifier, ResourceRequest&& request)
+WebURLSchemeTask::WebURLSchemeTask(WebURLSchemeHandler& handler, WebPageProxy& page, uint64_t resourceIdentifier, ResourceRequest&& request, SyncLoadCompletionHandler&& syncCompletionHandler)
     : m_urlSchemeHandler(handler)
     , m_page(&page)
     , m_identifier(resourceIdentifier)
     , m_pageIdentifier(page.pageID())
     , m_request(WTFMove(request))
+    , m_syncCompletionHandler(WTFMove(syncCompletionHandler))
 {
 }
 
@@ -63,6 +65,9 @@ auto WebURLSchemeTask::didPerformRedirection(WebCore::ResourceResponse&& respons
     if (m_responseSent)
         return ExceptionType::RedirectAfterResponse;
     
+    if (isSync())
+        m_syncResponse = response;
+
     m_request = request;
     m_page->send(Messages::WebPage::URLSchemeTaskDidPerformRedirection(m_urlSchemeHandler->identifier(), m_identifier, response, request));
 
@@ -83,6 +88,10 @@ auto WebURLSchemeTask::didReceiveResponse(const ResourceResponse& response) -> E
     m_responseSent = true;
 
     response.includeCertificateInfo();
+
+    if (isSync())
+        m_syncResponse = response;
+
     m_page->send(Messages::WebPage::URLSchemeTaskDidReceiveResponse(m_urlSchemeHandler->identifier(), m_identifier, response));
     return ExceptionType::None;
 }
@@ -99,6 +108,13 @@ auto WebURLSchemeTask::didReceiveData(Ref<SharedBuffer> buffer) -> ExceptionType
         return ExceptionType::NoResponseSent;
 
     m_dataSent = true;
+
+    if (isSync()) {
+        if (!m_syncData)
+            m_syncData = SharedBuffer::create();
+        m_syncData->append(buffer);
+    }
+
     m_page->send(Messages::WebPage::URLSchemeTaskDidReceiveData(m_urlSchemeHandler->identifier(), m_identifier, IPC::SharedBufferDataReference(buffer.ptr())));
     return ExceptionType::None;
 }
@@ -115,6 +131,12 @@ auto WebURLSchemeTask::didComplete(const ResourceError& error) -> ExceptionType
         return ExceptionType::NoResponseSent;
 
     m_completed = true;
+    
+    if (isSync()) {
+        m_syncCompletionHandler(m_syncResponse, error, IPC::DataReference { (const uint8_t*)m_syncData->data(), m_syncData->size() });
+        m_syncData = nullptr;
+    }
+
     m_page->send(Messages::WebPage::URLSchemeTaskDidComplete(m_urlSchemeHandler->identifier(), m_identifier, error));
     m_urlSchemeHandler->taskCompleted(*this);
 
@@ -126,12 +148,18 @@ void WebURLSchemeTask::pageDestroyed()
     ASSERT(m_page);
     m_page = nullptr;
     m_stopped = true;
+    
+    if (isSync())
+        m_syncCompletionHandler({ }, failedCustomProtocolSyncLoad(m_request), { });
 }
 
 void WebURLSchemeTask::stop()
 {
     ASSERT(!m_stopped);
     m_stopped = true;
+
+    if (isSync())
+        m_syncCompletionHandler({ }, failedCustomProtocolSyncLoad(m_request), { });
 }
 
 } // namespace WebKit
index 375474c..ad81dd5 100644 (file)
 #pragma once
 
 #include <WebCore/ResourceRequest.h>
+#include <WebCore/ResourceResponse.h>
+#include <WebCore/SharedBuffer.h>
+#include <wtf/CompletionHandler.h>
 #include <wtf/InstanceCounted.h>
 #include <wtf/RefCounted.h>
 #include <wtf/RefPtr.h>
 
+namespace IPC {
+class DataReference;
+}
+
 namespace WebCore {
 class ResourceError;
 class ResourceResponse;
@@ -41,10 +48,12 @@ namespace WebKit {
 class WebURLSchemeHandler;
 class WebPageProxy;
 
+using SyncLoadCompletionHandler = CompletionHandler<void(const WebCore::ResourceResponse&, const WebCore::ResourceError&, const IPC::DataReference&)>;
+
 class WebURLSchemeTask : public RefCounted<WebURLSchemeTask>, public InstanceCounted<WebURLSchemeTask> {
     WTF_MAKE_NONCOPYABLE(WebURLSchemeTask);
 public:
-    static Ref<WebURLSchemeTask> create(WebURLSchemeHandler&, WebPageProxy&, uint64_t identifier, WebCore::ResourceRequest&&);
+    static Ref<WebURLSchemeTask> create(WebURLSchemeHandler&, WebPageProxy&, uint64_t identifier, WebCore::ResourceRequest&&, SyncLoadCompletionHandler&&);
 
     uint64_t identifier() const { return m_identifier; }
     uint64_t pageID() const { return m_pageIdentifier; }
@@ -68,7 +77,9 @@ public:
     void pageDestroyed();
 
 private:
-    WebURLSchemeTask(WebURLSchemeHandler&, WebPageProxy&, uint64_t identifier, WebCore::ResourceRequest&&);
+    WebURLSchemeTask(WebURLSchemeHandler&, WebPageProxy&, uint64_t identifier, WebCore::ResourceRequest&&, SyncLoadCompletionHandler&&);
+
+    bool isSync() const { return !!m_syncCompletionHandler; }
 
     Ref<WebURLSchemeHandler> m_urlSchemeHandler;
     WebPageProxy* m_page;
@@ -79,6 +90,10 @@ private:
     bool m_responseSent { false };
     bool m_dataSent { false };
     bool m_completed { false };
+    
+    SyncLoadCompletionHandler m_syncCompletionHandler;
+    WebCore::ResourceResponse m_syncResponse;
+    RefPtr<WebCore::SharedBuffer> m_syncData;
 };
 
 } // namespace WebKit
index 95998a5..15cb99b 100644 (file)
@@ -473,6 +473,26 @@ static bool shouldClearReferrerOnHTTPSToHTTPRedirect(Frame* frame)
     return true;
 }
 
+std::optional<WebLoaderStrategy::SyncLoadResult> WebLoaderStrategy::tryLoadingSynchronouslyUsingURLSchemeHandler(FrameLoader& frameLoader, ResourceLoadIdentifier identifier, const ResourceRequest& request)
+{
+    auto* webFrameLoaderClient = toWebFrameLoaderClient(frameLoader.client());
+    auto* webFrame = webFrameLoaderClient ? webFrameLoaderClient->webFrame() : nullptr;
+    auto* webPage = webFrame ? webFrame->page() : nullptr;
+    if (!webPage)
+        return std::nullopt;
+
+    auto* handler = webPage->urlSchemeHandlerForScheme(request.url().protocol().toStringWithoutCopying());
+    if (!handler)
+        return std::nullopt;
+
+    LOG(NetworkScheduling, "(WebProcess) WebLoaderStrategy::scheduleLoad, sync load to URL '%s' will be handled by a UIProcess URL scheme handler.", request.url().string().utf8().data());
+
+    SyncLoadResult result;
+    handler->loadSynchronously(identifier, request, result.response, result.error, result.data);
+
+    return WTFMove(result);
+}
+
 void WebLoaderStrategy::loadResourceSynchronously(FrameLoader& frameLoader, unsigned long resourceLoadIdentifier, const ResourceRequest& request, ClientCredentialPolicy clientCredentialPolicy,  const FetchOptions& options, const HTTPHeaderMap& originalRequestHeaders, ResourceError& error, ResourceResponse& response, Vector<char>& data)
 {
     auto* document = frameLoader.frame().document();
@@ -481,6 +501,13 @@ void WebLoaderStrategy::loadResourceSynchronously(FrameLoader& frameLoader, unsi
         return;
     }
 
+    if (auto syncLoadResult = tryLoadingSynchronouslyUsingURLSchemeHandler(frameLoader, resourceLoadIdentifier, request)) {
+        error = WTFMove(syncLoadResult->error);
+        response = WTFMove(syncLoadResult->response);
+        data = WTFMove(syncLoadResult->data);
+        return;
+    }
+
     WebFrameLoaderClient* webFrameLoaderClient = toWebFrameLoaderClient(frameLoader.client());
     WebFrame* webFrame = webFrameLoaderClient ? webFrameLoaderClient->webFrame() : nullptr;
     WebPage* webPage = webFrame ? webFrame->page() : nullptr;
index 0983f42..5b7e97f 100644 (file)
@@ -27,7 +27,9 @@
 
 #include "WebResourceLoader.h"
 #include <WebCore/LoaderStrategy.h>
+#include <WebCore/ResourceError.h>
 #include <WebCore/ResourceLoader.h>
+#include <WebCore/ResourceResponse.h>
 #include <wtf/HashSet.h>
 #include <wtf/RunLoop.h>
 
@@ -90,6 +92,13 @@ private:
     void internallyFailedLoadTimerFired();
     void startLocalLoad(WebCore::ResourceLoader&);
     bool tryLoadingUsingURLSchemeHandler(WebCore::ResourceLoader&);
+    
+    struct SyncLoadResult {
+        WebCore::ResourceResponse response;
+        WebCore::ResourceError error;
+        Vector<char> data;
+    };
+    std::optional<SyncLoadResult> tryLoadingSynchronouslyUsingURLSchemeHandler(WebCore::FrameLoader&, ResourceLoadIdentifier, const WebCore::ResourceRequest&);
 
     WebCore::ResourceResponse responseFromResourceLoadIdentifier(uint64_t resourceLoadIdentifier) final;
     Vector<WebCore::NetworkTransactionInformation> intermediateLoadInformationFromResourceLoadIdentifier(uint64_t resourceLoadIdentifier) final;
index cdb43fb..ad2026a 100644 (file)
 #include "config.h"
 #include "WebURLSchemeHandlerProxy.h"
 
+#include "DataReference.h"
+#include "URLSchemeTaskParameters.h"
+#include "WebCoreArgumentCoders.h"
 #include "WebErrors.h"
 #include "WebLoaderStrategy.h"
+#include "WebPage.h"
+#include "WebPageProxyMessages.h"
 #include "WebProcess.h"
+#include <WebCore/ResourceError.h>
 #include <WebCore/ResourceLoader.h>
+#include <WebCore/ResourceRequest.h>
+#include <WebCore/ResourceResponse.h>
 
 using namespace WebCore;
 
@@ -55,6 +63,18 @@ void WebURLSchemeHandlerProxy::startNewTask(ResourceLoader& loader)
     result.iterator->value->startLoading();
 }
 
+void WebURLSchemeHandlerProxy::loadSynchronously(ResourceLoadIdentifier loadIdentifier, const ResourceRequest& request, ResourceResponse& response, ResourceError& error, Vector<char>& data)
+{
+    IPC::DataReference dataReference;
+    if (!m_webPage.sendSync(Messages::WebPageProxy::LoadSynchronousURLSchemeTask(URLSchemeTaskParameters { m_identifier, loadIdentifier, request }), Messages::WebPageProxy::LoadSynchronousURLSchemeTask::Reply(response, error, dataReference))) {
+        error = failedCustomProtocolSyncLoad(request);
+        return;
+    }
+    
+    data.resize(dataReference.size());
+    memcpy(data.data(), dataReference.data(), dataReference.size());
+}
+
 void WebURLSchemeHandlerProxy::stopAllTasks()
 {
     while (!m_tasks.isEmpty())
index 742415c..d8e377a 100644 (file)
@@ -39,6 +39,7 @@ class ResourceRequest;
 namespace WebKit {
 
 class WebPage;
+typedef uint64_t ResourceLoadIdentifier;
 
 class WebURLSchemeHandlerProxy : public RefCounted<WebURLSchemeHandlerProxy> {
 public:
@@ -51,6 +52,8 @@ public:
     void startNewTask(WebCore::ResourceLoader&);
     void stopAllTasks();
 
+    void loadSynchronously(ResourceLoadIdentifier, const WebCore::ResourceRequest&, WebCore::ResourceResponse&, WebCore::ResourceError&, Vector<char>&);
+
     uint64_t identifier() const { return m_identifier; }
     WebPage& page() { return m_webPage; }
 
index e59e51b..646196e 100644 (file)
@@ -1,3 +1,16 @@
+2018-06-22  Brady Eidson  <beidson@apple.com>
+
+        WKURLSchemeHandler doesn't handle sync XHR.
+        <rdar://problem/40955884> and https://bugs.webkit.org/show_bug.cgi?id=186902
+
+        Reviewed by Chris Dumez.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKURLSchemeHandler-1.mm:
+        (-[SyncScheme webView:startURLSchemeTask:]):
+        (-[SyncScheme webView:stopURLSchemeTask:]):
+        (-[SyncMessageHandler userContentController:didReceiveScriptMessage:]):
+        (catch):
+
 2018-06-22  Daniel Bates  <dabates@apple.com>
 
         Security EWS: bots fails with exception 'NoneType' object has no attribute 'is_closed'
index 412e77f..f0e6897 100644 (file)
 #import <WebKit/WKURLSchemeTaskPrivate.h>
 #import <WebKit/WKWebViewConfigurationPrivate.h>
 #import <WebKit/WebKit.h>
+#import <wtf/HashMap.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/Vector.h>
+#import <wtf/text/StringHash.h>
+#import <wtf/text/WTFString.h>
 
 #if WK_API_ENABLED
 
@@ -409,4 +412,131 @@ TEST(URLSchemeHandler, Exceptions)
     checkCallSequence({Command::Response, Command::Finish, Command::Error}, ShouldRaiseException::Yes);
 }
 
-#endif
+struct SchemeResourceInfo {
+    RetainPtr<NSString> mimeType;
+    const char* data;
+    bool shouldRespond;
+};
+
+static bool startedXHR;
+static bool receivedStop;
+
+@interface SyncScheme : NSObject <WKURLSchemeHandler> {
+@public
+    HashMap<String, SchemeResourceInfo> resources;
+}
+@end
+
+@implementation SyncScheme
+
+- (void)webView:(WKWebView *)webView startURLSchemeTask:(id <WKURLSchemeTask>)task
+{
+    auto entry = resources.find([task.request.URL absoluteString]);
+    if (entry == resources.end()) {
+        NSLog(@"Did not find resource entry for URL %@", task.request.URL);
+        return;
+    }
+
+    if (entry->key == "syncxhr://host/test.dat")
+        startedXHR = true;
+
+    if (!entry->value.shouldRespond)
+        return;
+    
+    RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:entry->value.mimeType.get() expectedContentLength:1 textEncodingName:nil]);
+    [task didReceiveResponse:response.get()];
+
+    [task didReceiveData:[NSData dataWithBytesNoCopy:(void*)entry->value.data length:strlen(entry->value.data) freeWhenDone:NO]];
+    [task didFinish];
+
+    if (entry->key == "syncxhr://host/test.dat")
+        startedXHR = false;
+}
+
+- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
+{
+    EXPECT_TRUE([[task.request.URL absoluteString] isEqualToString:@"syncxhr://host/test.dat"]);
+    receivedStop = true;
+}
+
+@end
+
+static RetainPtr<NSMutableArray> receivedMessages = adoptNS([@[] mutableCopy]);
+static bool receivedMessage;
+
+@interface SyncMessageHandler : NSObject <WKScriptMessageHandler>
+@end
+
+@implementation SyncMessageHandler
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+    if ([message body])
+        [receivedMessages addObject:[message body]];
+    else
+        [receivedMessages addObject:@""];
+
+    receivedMessage = true;
+}
+@end
+
+static const char* syncMainBytes = R"SYNCRESOURCE(
+<script>
+
+var req = new XMLHttpRequest();
+req.open("GET", "test.dat", false);
+try
+{
+    req.send(null);
+    window.webkit.messageHandlers.sync.postMessage(req.responseText);
+}
+catch (e)
+{
+    window.webkit.messageHandlers.sync.postMessage("Failed sync XHR load");
+}
+
+</script>
+)SYNCRESOURCE";
+
+static const char* syncXHRBytes = "My XHR text!";
+
+TEST(URLSchemeHandler, SyncXHR)
+{
+    auto *pool = [[NSAutoreleasePool alloc] init];
+
+    auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto handler = adoptNS([[SyncScheme alloc] init]);
+    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"syncxhr"];
+    
+    handler.get()->resources.set("syncxhr://host/main.html", SchemeResourceInfo { @"text/html", syncMainBytes, true });
+    handler.get()->resources.set("syncxhr://host/test.dat", SchemeResourceInfo { @"text/plain", syncXHRBytes, true });
+
+    auto messageHandler = adoptNS([[SyncMessageHandler alloc] init]);
+    [[webViewConfiguration userContentController] addScriptMessageHandler:messageHandler.get() name:@"sync"];
+    
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"syncxhr://host/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&receivedMessage);
+    receivedMessage = false;
+
+    EXPECT_EQ((unsigned)receivedMessages.get().count, (unsigned)1);
+    EXPECT_TRUE([receivedMessages.get()[0] isEqualToString:@"My XHR text!"]);
+
+    // Now try again, but hang the WebProcess in the reply to the XHR by telling the scheme handler to never
+    // respond to it.
+    handler.get()->resources.find("syncxhr://host/test.dat")->value.shouldRespond = false;
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&startedXHR);
+    receivedMessage = false;
+
+    webView = nil;
+    [pool drain];
+    
+    TestWebKitAPI::Util::run(&receivedStop);
+}
+
+#endif // WK_API_ENABLED
+