Diff
Modified: trunk/Source/WebKit2/ChangeLog (193829 => 193830)
--- trunk/Source/WebKit2/ChangeLog 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Source/WebKit2/ChangeLog 2015-12-09 14:52:46 UTC (rev 193830)
@@ -1,3 +1,35 @@
+2015-12-09 Mario Sanchez Prada <ma...@endlessm.com>
+
+ [GTK] Crash in WebProcess when loading large content with custom URI schemes
+ https://bugs.webkit.org/show_bug.cgi?id=144262
+
+ Reviewed by Carlos Garcia Campos.
+
+ Properly handle scenarios where errors happen after reading the first
+ chunk of data coming from the GInputStream provided by the application.
+
+ * UIProcess/API/gtk/WebKitWebContextPrivate.h:
+ * UIProcess/API/gtk/WebKitWebContext.cpp:
+ (webkitWebContextIsLoadingCustomProtocol): New, checks whether a load
+ is still in progress, after the startLoading method has been called.
+ * UIProcess/API/gtk/WebKitURISchemeRequest.cpp:
+ (webkitURISchemeRequestReadCallback): Early return if the stream has been
+ cancelled on finish_error, so that we make sure we don't keep on reading
+ the GInputStream after that point.
+ (webkit_uri_scheme_request_finish_error): Don't send a didFailWithError
+ message to the Network process if the load is not longer in progress.
+ * Shared/Network/CustomProtocols/soup/CustomProtocolManagerImpl.cpp:
+ (WebKit::CustomProtocolManagerImpl::didFailWithError): Handle the case where
+ an error is notified from the UI process after the first chunk has been read.
+ (WebKit::CustomProtocolManagerImpl::didReceiveResponse): Handle the case where
+ data might no longer be available if an error happened even before this point.
+ * WebProcess/soup/WebKitSoupRequestInputStream.h:
+ * WebProcess/soup/WebKitSoupRequestInputStream.cpp:
+ (webkitSoupRequestInputStreamDidFailWithError): Notify the custom GInputStream
+ that we no longer want to keep reading data in chunks due to a specific error.
+ (webkitSoupRequestInputStreamReadAsync): Early finish the GTask with a specific
+ error whenever webkitSoupRequestInputStreamDidFailWithError() has been called.
+
2015-12-09 Ryuan Choi <ryuan.c...@navercorp.com>
[CoordinatedGraphics][EFL] Fix unhandled web process message when launching MiniBrowser
Modified: trunk/Source/WebKit2/Shared/Network/CustomProtocols/soup/CustomProtocolManagerImpl.cpp (193829 => 193830)
--- trunk/Source/WebKit2/Shared/Network/CustomProtocols/soup/CustomProtocolManagerImpl.cpp 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Source/WebKit2/Shared/Network/CustomProtocols/soup/CustomProtocolManagerImpl.cpp 2015-12-09 14:52:46 UTC (rev 193830)
@@ -116,11 +116,18 @@
WebSoupRequestAsyncData* data = ""
ASSERT(data);
- GRefPtr<GTask> task = data->releaseTask();
- ASSERT(task.get());
- g_task_return_new_error(task.get(), g_quark_from_string(error.domain().utf8().data()),
- error.errorCode(), "%s", error.localizedDescription().utf8().data());
+ // Either we haven't started reading the stream yet, in which case we need to complete the
+ // task first, or we failed reading it and the task was already completed by didLoadData().
+ ASSERT(!data->stream || !data->task);
+ if (!data->stream) {
+ GRefPtr<GTask> task = data->releaseTask();
+ ASSERT(task.get());
+ g_task_return_new_error(task.get(), g_quark_from_string(error.domain().utf8().data()),
+ error.errorCode(), "%s", error.localizedDescription().utf8().data());
+ } else
+ webkitSoupRequestInputStreamDidFailWithError(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), error);
+
m_customProtocolMap.remove(customProtocolID);
}
@@ -170,7 +177,10 @@
void CustomProtocolManagerImpl::didReceiveResponse(uint64_t customProtocolID, const WebCore::ResourceResponse& response)
{
WebSoupRequestAsyncData* data = ""
- ASSERT(data);
+ // The data might have been removed from the request map if an error happened even before this point.
+ if (!data)
+ return;
+
ASSERT(data->task);
WebKitSoupRequestGeneric* request = WEBKIT_SOUP_REQUEST_GENERIC(g_task_get_source_object(data->task));
Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitURISchemeRequest.cpp (193829 => 193830)
--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitURISchemeRequest.cpp 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitURISchemeRequest.cpp 2015-12-09 14:52:46 UTC (rev 193830)
@@ -166,6 +166,11 @@
return;
}
+ // Need to check the stream before proceeding as it can be cancelled if finish_error
+ // was previously call, which won't be detected by g_input_stream_read_finish().
+ if (!request->priv->stream)
+ return;
+
WebKitURISchemeRequestPrivate* priv = request->priv;
Ref<API::Data> webData = API::Data::create(reinterpret_cast<const unsigned char*>(priv->readBuffer), bytesRead);
if (!priv->bytesRead) {
@@ -230,7 +235,10 @@
g_return_if_fail(error);
WebKitURISchemeRequestPrivate* priv = request->priv;
+ if (!webkitWebContextIsLoadingCustomProtocol(priv->webContext, priv->requestID))
+ return;
+ priv->stream = nullptr;
WebCore::ResourceError resourceError(g_quark_to_string(error->domain), toWebCoreError(error->code), priv->uri.data(), String::fromUTF8(error->message));
priv->webRequestManager->didFailWithError(priv->requestID, resourceError);
webkitWebContextDidFinishLoadingCustomProtocol(priv->webContext, priv->requestID);
Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp (193829 => 193830)
--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp 2015-12-09 14:52:46 UTC (rev 193830)
@@ -1294,6 +1294,11 @@
context->priv->uriSchemeRequests.remove(customProtocolID);
}
+bool webkitWebContextIsLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID)
+{
+ return context->priv->uriSchemeRequests.get(customProtocolID);
+}
+
void webkitWebContextCreatePageForWebView(WebKitWebContext* context, WebKitWebView* webView, WebKitUserContentManager* userContentManager, WebKitWebView* relatedView)
{
WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(webView);
Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContextPrivate.h (193829 => 193830)
--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContextPrivate.h 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebContextPrivate.h 2015-12-09 14:52:46 UTC (rev 193830)
@@ -42,6 +42,7 @@
void webkitWebContextStartLoadingCustomProtocol(WebKitWebContext*, uint64_t customProtocolID, API::URLRequest*);
void webkitWebContextStopLoadingCustomProtocol(WebKitWebContext*, uint64_t customProtocolID);
void webkitWebContextDidFinishLoadingCustomProtocol(WebKitWebContext*, uint64_t customProtocolID);
+bool webkitWebContextIsLoadingCustomProtocol(WebKitWebContext*, uint64_t customProtocolID);
void webkitWebContextCreatePageForWebView(WebKitWebContext*, WebKitWebView*, WebKitUserContentManager*, WebKitWebView*);
void webkitWebContextWebViewDestroyed(WebKitWebContext*, WebKitWebView*);
WebKitWebView* webkitWebContextGetWebViewForPage(WebKitWebContext*, WebKit::WebPageProxy*);
Modified: trunk/Source/WebKit2/WebProcess/soup/WebKitSoupRequestInputStream.cpp (193829 => 193830)
--- trunk/Source/WebKit2/WebProcess/soup/WebKitSoupRequestInputStream.cpp 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Source/WebKit2/WebProcess/soup/WebKitSoupRequestInputStream.cpp 2015-12-09 14:52:46 UTC (rev 193830)
@@ -23,6 +23,7 @@
#include <wtf/Lock.h>
#include <wtf/Threading.h>
#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
struct AsyncReadData {
AsyncReadData(GTask* task, void* buffer, gsize count)
@@ -42,6 +43,8 @@
uint64_t bytesReceived;
uint64_t bytesRead;
+ GUniquePtr<GError> error;
+
Lock readLock;
std::unique_ptr<AsyncReadData> pendingAsyncRead;
};
@@ -92,6 +95,11 @@
return;
}
+ if (stream->priv->error.get()) {
+ g_task_return_error(task.get(), stream->priv->error.release());
+ return;
+ }
+
if (webkitSoupRequestInputStreamHasDataToRead(stream)) {
webkitSoupRequestInputStreamReadAsyncResultComplete(task.get(), buffer, count);
return;
@@ -163,6 +171,18 @@
webkitSoupRequestInputStreamPendingReadAsyncComplete(stream);
}
+void webkitSoupRequestInputStreamDidFailWithError(WebKitSoupRequestInputStream* stream, const WebCore::ResourceError& resourceError)
+{
+ GUniquePtr<GError> error(g_error_new(g_quark_from_string(resourceError.domain().utf8().data()), resourceError.errorCode(), "%s", resourceError.localizedDescription().utf8().data()));
+ if (stream->priv->pendingAsyncRead) {
+ AsyncReadData* data = ""
+ g_task_return_error(data->task.get(), error.release());
+ } else {
+ stream->priv->contentLength = stream->priv->bytesReceived;
+ stream->priv->error = WTF::move(error);
+ }
+}
+
bool webkitSoupRequestInputStreamFinished(WebKitSoupRequestInputStream* stream)
{
return !webkitSoupRequestInputStreamIsWaitingForData(stream);
Modified: trunk/Source/WebKit2/WebProcess/soup/WebKitSoupRequestInputStream.h (193829 => 193830)
--- trunk/Source/WebKit2/WebProcess/soup/WebKitSoupRequestInputStream.h 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Source/WebKit2/WebProcess/soup/WebKitSoupRequestInputStream.h 2015-12-09 14:52:46 UTC (rev 193830)
@@ -20,6 +20,7 @@
#ifndef WebKitSoupRequestInputStream_h
#define WebKitSoupRequestInputStream_h
+#include <WebCore/ResourceError.h>
#include <gio/gio.h>
G_BEGIN_DECLS
@@ -48,6 +49,7 @@
GType webkit_soup_request_input_stream_get_type();
GInputStream* webkitSoupRequestInputStreamNew(uint64_t contentLength);
void webkitSoupRequestInputStreamAddData(WebKitSoupRequestInputStream*, const void* data, size_t dataLength);
+void webkitSoupRequestInputStreamDidFailWithError(WebKitSoupRequestInputStream*, const WebCore::ResourceError&);
bool webkitSoupRequestInputStreamFinished(WebKitSoupRequestInputStream*);
G_END_DECLS
Modified: trunk/Tools/ChangeLog (193829 => 193830)
--- trunk/Tools/ChangeLog 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Tools/ChangeLog 2015-12-09 14:52:46 UTC (rev 193830)
@@ -1,3 +1,17 @@
+2015-12-09 Mario Sanchez Prada <ma...@endlessm.com>
+
+ [GTK] Crash in WebProcess when loading large content with custom URI schemes
+ https://bugs.webkit.org/show_bug.cgi?id=144262
+
+ Reviewed by Carlos Garcia Campos.
+
+ Added new unit test to check the additional scenarios we now
+ handle for custom URI schemes.
+
+ * TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp:
+ (generateHTMLContent): New helper function to generate big enough content.
+ (testWebContextURIScheme): New unit test.
+
2015-12-09 Ryuan Choi <ryuan.c...@navercorp.com>
[EFL] Fix unhandled web process message when launching MiniBrowser
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp (193829 => 193830)
--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp 2015-12-09 14:46:11 UTC (rev 193829)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp 2015-12-09 14:52:46 UTC (rev 193830)
@@ -26,6 +26,7 @@
#include <wtf/HashMap.h>
#include <wtf/glib/GRefPtr.h>
#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/StringBuilder.h>
#include <wtf/text/StringHash.h>
static WebKitTestServer* kServer;
@@ -197,8 +198,11 @@
static const char* kEchoHTMLFormat = "<html><body>%s</body></html>";
static const char* errorDomain = "test";
static const int errorCode = 10;
-static const char* errorMessage = "Error message.";
+static const char* genericErrorMessage = "Error message.";
+static const char* beforeReceiveResponseErrorMessage = "Error before didReceiveResponse.";
+static const char* afterInitialChunkErrorMessage = "Error after reading the initial chunk.";
+
class URISchemeTest: public LoadTrackingTest {
public:
MAKE_GLIB_TEST_FIXTURE(URISchemeTest);
@@ -229,23 +233,38 @@
g_assert(webkit_uri_scheme_request_get_web_view(request) == test->m_webView);
- GRefPtr<GInputStream> inputStream = adoptGRef(g_memory_input_stream_new());
- test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inputStream.get()));
-
const char* scheme = webkit_uri_scheme_request_get_scheme(request);
g_assert(scheme);
g_assert(test->m_handlersMap.contains(String::fromUTF8(scheme)));
+ const URISchemeHandler& handler = test->m_handlersMap.get(String::fromUTF8(scheme));
+
+ GRefPtr<GInputStream> inputStream = adoptGRef(g_memory_input_stream_new());
+ test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(inputStream.get()));
+
+ const gchar* requestPath = webkit_uri_scheme_request_get_path(request);
+
if (!g_strcmp0(scheme, "error")) {
- GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, errorMessage));
- webkit_uri_scheme_request_finish_error(request, error.get());
+ if (!g_strcmp0(requestPath, "before-response")) {
+ GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, beforeReceiveResponseErrorMessage));
+ // We call finish() and then finish_error() to make sure that not even
+ // the didReceiveResponse message is processed at the time of failing.
+ webkit_uri_scheme_request_finish(request, G_INPUT_STREAM(inputStream.get()), handler.replyLength, handler.mimeType.data());
+ webkit_uri_scheme_request_finish_error(request, error.get());
+ } else if (!g_strcmp0(requestPath, "after-first-chunk")) {
+ g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), handler.reply.data(), handler.reply.length(), 0);
+ webkit_uri_scheme_request_finish(request, inputStream.get(), handler.replyLength, handler.mimeType.data());
+ // We need to wait until we reach the load-committed state before calling webkit_uri_scheme_request_finish_error(),
+ // so we rely on the test using finishOnCommittedAndWaitUntilLoadFinished() to actually call it from loadCommitted().
+ } else {
+ GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, genericErrorMessage));
+ webkit_uri_scheme_request_finish_error(request, error.get());
+ }
return;
}
- const URISchemeHandler& handler = test->m_handlersMap.get(String::fromUTF8(scheme));
-
if (!g_strcmp0(scheme, "echo")) {
- char* replyHTML = g_strdup_printf(handler.reply.data(), webkit_uri_scheme_request_get_path(request));
+ char* replyHTML = g_strdup_printf(handler.reply.data(), requestPath);
g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(inputStream.get()), replyHTML, strlen(replyHTML), g_free);
} else if (!g_strcmp0(scheme, "closed"))
g_input_stream_close(inputStream.get(), 0, 0);
@@ -261,10 +280,55 @@
webkit_web_context_register_uri_scheme(m_webContext.get(), scheme, uriSchemeRequestCallback, this, 0);
}
+ virtual void loadCommitted() override
+ {
+ if (m_finishOnCommitted) {
+ GUniquePtr<GError> error(g_error_new_literal(g_quark_from_string(errorDomain), errorCode, afterInitialChunkErrorMessage));
+ webkit_uri_scheme_request_finish_error(m_uriSchemeRequest.get(), error.get());
+ }
+
+ LoadTrackingTest::loadCommitted();
+ }
+
+ void finishOnCommittedAndWaitUntilLoadFinished()
+ {
+ m_finishOnCommitted = true;
+ waitUntilLoadFinished();
+ m_finishOnCommitted = false;
+ }
+
GRefPtr<WebKitURISchemeRequest> m_uriSchemeRequest;
HashMap<String, URISchemeHandler> m_handlersMap;
+ bool m_finishOnCommitted { false };
};
+String generateHTMLContent(unsigned contentLength)
+{
+ String baseString("abcdefghijklmnopqrstuvwxyz0123457890");
+ unsigned baseLength = baseString.length();
+
+ StringBuilder builder;
+ builder.append("<html><body>");
+
+ if (contentLength <= baseLength)
+ builder.append(baseString, 0, contentLength);
+ else {
+ unsigned currentLength = 0;
+ while (currentLength < contentLength) {
+ if ((currentLength + baseLength) <= contentLength)
+ builder.append(baseString);
+ else
+ builder.append(baseString, 0, contentLength - currentLength);
+
+ // Account for the 12 characters of the '<html><body>' prefix.
+ currentLength = builder.length() - 12;
+ }
+ }
+ builder.append("</body></html>");
+
+ return builder.toString();
+}
+
static void testWebContextURIScheme(URISchemeTest* test, gconstpointer)
{
test->registerURISchemeHandler("foo", kBarHTML, strlen(kBarHTML), "text/html");
@@ -307,15 +371,36 @@
g_assert(!test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
g_assert(!test->m_loadEvents.contains(LoadTrackingTest::LoadFailed));
- test->registerURISchemeHandler("error", 0, 0, 0);
+ // Anything over 8192 bytes will get multiple calls to g_input_stream_read_async in
+ // WebKitURISchemeRequest when reading data, but we still need way more than that to
+ // ensure that we reach the load-committed state before failing, so we use an 8MB HTML.
+ String longHTMLContent = generateHTMLContent(8 * 1024 * 1024);
+ test->registerURISchemeHandler("error", longHTMLContent.utf8().data(), -1, "text/html");
test->m_loadEvents.clear();
test->loadURI("error:error");
test->waitUntilLoadFinished();
g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
g_assert(test->m_loadFailed);
g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode);
- g_assert_cmpstr(test->m_error->message, ==, errorMessage);
+ g_assert_cmpstr(test->m_error->message, ==, genericErrorMessage);
+ test->m_loadEvents.clear();
+ test->loadURI("error:before-response");
+ test->waitUntilLoadFinished();
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(test->m_loadFailed);
+ g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode);
+ g_assert_cmpstr(test->m_error->message, ==, beforeReceiveResponseErrorMessage);
+
+ test->m_loadEvents.clear();
+ test->loadURI("error:after-first-chunk");
+ test->finishOnCommittedAndWaitUntilLoadFinished();
+ g_assert(!test->m_loadEvents.contains(LoadTrackingTest::ProvisionalLoadFailed));
+ g_assert(test->m_loadEvents.contains(LoadTrackingTest::LoadFailed));
+ g_assert(test->m_loadFailed);
+ g_assert_error(test->m_error.get(), g_quark_from_string(errorDomain), errorCode);
+ g_assert_cmpstr(test->m_error->message, ==, afterInitialChunkErrorMessage);
+
test->registerURISchemeHandler("closed", 0, 0, 0);
test->m_loadEvents.clear();
test->loadURI("closed:input-stream");