[GTK] Implement DownloadClient in WebKit2 GTK+ API
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jan 2012 09:45:28 +0000 (09:45 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jan 2012 09:45:28 +0000 (09:45 +0000)
https://bugs.webkit.org/show_bug.cgi?id=72952

Reviewed by Martin Robinson.

Source/WebKit2:

* GNUmakefile.am: Add new files to compilation.
* UIProcess/API/gtk/WebKitDownloadClient.cpp: Added.
(didStart): Call webkitWebContextDownloadStarted().
(didReceiveResponse): Call webkitDownloadSetResponse() with the
received response.
(didReceiveData): Call webkitDownloadNotifyProgress().
(decideDestinationWithSuggestedFilename): Call
webkitDownloadDecideDestinationWithSuggestedFilename().
(didCreateDestination): Call webkitDownloadDestinationCreated().
(didFail): Call webkitDownloadFailed() or
webkitDownloadCancelled() if the download was cancelled before
failing.
(didCancel): Call webkitDownloadCancelled().
(didFinish): Call webkitDownloadFinished().
(attachDownloadClientToContext): Add
implementation for download client callbacks.
* UIProcess/API/gtk/WebKitDownloadClient.h: Added.
* UIProcess/API/gtk/WebKitError.cpp:
(webkit_download_error_quark): Add quark for download errors.
* UIProcess/API/gtk/WebKitError.h:
* UIProcess/API/gtk/WebKitWebContext.cpp:
(webkit_web_context_class_init): Add download-started signal.
(createDefaultWebContext): Initialize the download client.
(downloadsMap): HashMap containing download objects for all
ongoing download operations.
(webkit_web_context_download_uri): Start a new download for the
given URI.
(webkitWebContextGetOrCreateDownload): Helper function to create a
new download object or return the existing one from the downloads map.
(webkitWebContextRemoveDownload): Remove the download object from
the downloads map.
(webkitWebContextDownloadStarted): Emit
WebKitWebContext::download-started for the given download object.
* UIProcess/API/gtk/WebKitWebContext.h:
* UIProcess/API/gtk/WebKitWebContextPrivate.h:
* UIProcess/API/gtk/docs/webkit2gtk-sections.txt: Add new symbols.
* UIProcess/API/gtk/tests/GNUmakefile.am: Add new test for downloads.
* UIProcess/API/gtk/tests/TestDownloads.cpp: Added.
(getWebKit1TestResoucesDir):
(testDownloadLocalFile):
(testDownloadLocalFileError):
(serverCallback):
(testDownloadRemoteFile):
(testDownloadRemoteFileError):
(beforeAll):
(afterAll):

Tools:

* gtk/generate-gtkdoc:
(get_webkit2_options): Ignore WebKitDownloadClient.

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

14 files changed:
Source/WebKit2/ChangeLog
Source/WebKit2/GNUmakefile.am
Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.cpp [new file with mode: 0644]
Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.h [new file with mode: 0644]
Source/WebKit2/UIProcess/API/gtk/WebKitError.cpp
Source/WebKit2/UIProcess/API/gtk/WebKitError.h
Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.cpp
Source/WebKit2/UIProcess/API/gtk/WebKitWebContext.h
Source/WebKit2/UIProcess/API/gtk/WebKitWebContextPrivate.h
Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-sections.txt
Source/WebKit2/UIProcess/API/gtk/tests/GNUmakefile.am
Source/WebKit2/UIProcess/API/gtk/tests/TestDownloads.cpp [new file with mode: 0644]
Tools/ChangeLog
Tools/gtk/generate-gtkdoc

index da52816..7b9b3cd 100644 (file)
@@ -1,3 +1,57 @@
+2012-01-24  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK] Implement DownloadClient in WebKit2 GTK+ API
+        https://bugs.webkit.org/show_bug.cgi?id=72952
+
+        Reviewed by Martin Robinson.
+
+        * GNUmakefile.am: Add new files to compilation.
+        * UIProcess/API/gtk/WebKitDownloadClient.cpp: Added.
+        (didStart): Call webkitWebContextDownloadStarted().
+        (didReceiveResponse): Call webkitDownloadSetResponse() with the
+        received response.
+        (didReceiveData): Call webkitDownloadNotifyProgress().
+        (decideDestinationWithSuggestedFilename): Call
+        webkitDownloadDecideDestinationWithSuggestedFilename().
+        (didCreateDestination): Call webkitDownloadDestinationCreated().
+        (didFail): Call webkitDownloadFailed() or
+        webkitDownloadCancelled() if the download was cancelled before
+        failing.
+        (didCancel): Call webkitDownloadCancelled().
+        (didFinish): Call webkitDownloadFinished().
+        (attachDownloadClientToContext): Add
+        implementation for download client callbacks.
+        * UIProcess/API/gtk/WebKitDownloadClient.h: Added.
+        * UIProcess/API/gtk/WebKitError.cpp:
+        (webkit_download_error_quark): Add quark for download errors.
+        * UIProcess/API/gtk/WebKitError.h:
+        * UIProcess/API/gtk/WebKitWebContext.cpp:
+        (webkit_web_context_class_init): Add download-started signal.
+        (createDefaultWebContext): Initialize the download client.
+        (downloadsMap): HashMap containing download objects for all
+        ongoing download operations.
+        (webkit_web_context_download_uri): Start a new download for the
+        given URI.
+        (webkitWebContextGetOrCreateDownload): Helper function to create a
+        new download object or return the existing one from the downloads map.
+        (webkitWebContextRemoveDownload): Remove the download object from
+        the downloads map.
+        (webkitWebContextDownloadStarted): Emit
+        WebKitWebContext::download-started for the given download object.
+        * UIProcess/API/gtk/WebKitWebContext.h:
+        * UIProcess/API/gtk/WebKitWebContextPrivate.h:
+        * UIProcess/API/gtk/docs/webkit2gtk-sections.txt: Add new symbols.
+        * UIProcess/API/gtk/tests/GNUmakefile.am: Add new test for downloads.
+        * UIProcess/API/gtk/tests/TestDownloads.cpp: Added.
+        (getWebKit1TestResoucesDir):
+        (testDownloadLocalFile):
+        (testDownloadLocalFileError):
+        (serverCallback):
+        (testDownloadRemoteFile):
+        (testDownloadRemoteFileError):
+        (beforeAll):
+        (afterAll):
+
 2012-01-23  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         [GTK] Add WebKitDownload to WebKit2 GTK+ API
index b5f5624..9200262 100644 (file)
@@ -525,6 +525,8 @@ libwebkit2gtk_@WEBKITGTK_API_MAJOR_VERSION@_@WEBKITGTK_API_MINOR_VERSION@_la_SOU
        Source/WebKit2/UIProcess/API/gtk/WebKitDownload.cpp \
        Source/WebKit2/UIProcess/API/gtk/WebKitDownload.h \
        Source/WebKit2/UIProcess/API/gtk/WebKitDownloadPrivate.h \
+       Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.cpp \
+       Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.h \
        Source/WebKit2/UIProcess/API/gtk/WebKitError.h \
        Source/WebKit2/UIProcess/API/gtk/WebKitError.cpp \
        Source/WebKit2/UIProcess/API/gtk/WebKitPrivate.h \
diff --git a/Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.cpp b/Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.cpp
new file mode 100644 (file)
index 0000000..6e72496
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitDownloadClient.h"
+
+#include "WebContext.h"
+#include "WebKitDownloadPrivate.h"
+#include "WebKitURIResponsePrivate.h"
+#include "WebKitWebContextPrivate.h"
+#include "WebURLResponse.h"
+#include <WebKit2/WKString.h>
+#include <wtf/gobject/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+using namespace WebCore;
+using namespace WebKit;
+
+static void didStart(WKContextRef, WKDownloadRef wkDownload, const void* clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    webkitWebContextDownloadStarted(WEBKIT_WEB_CONTEXT(clientInfo), download.get());
+}
+
+static void didReceiveResponse(WKContextRef, WKDownloadRef wkDownload, WKURLResponseRef wkResponse, const void* clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    if (webkitDownloadIsCancelled(download.get()))
+        return;
+
+    GRefPtr<WebKitURIResponse> response = adoptGRef(webkitURIResponseCreateForResourceResponse(toImpl(wkResponse)->resourceResponse()));
+    webkitDownloadSetResponse(download.get(), response.get());
+}
+
+static void didReceiveData(WKContextRef, WKDownloadRef wkDownload, uint64_t length, const void* clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    webkitDownloadNotifyProgress(download.get(), length);
+}
+
+static WKStringRef decideDestinationWithSuggestedFilename(WKContextRef, WKDownloadRef wkDownload, WKStringRef filename, bool* allowOverwrite, const void* clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    CString destinationURI = webkitDownloadDecideDestinationWithSuggestedFilename(download.get(),
+                                                                                  toImpl(filename)->string().utf8());
+    return WKStringCreateWithUTF8CString(destinationURI.data());
+}
+
+static void didCreateDestination(WKContextRef, WKDownloadRef wkDownload, WKStringRef path, const void* clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    webkitDownloadDestinationCreated(download.get(), toImpl(path)->string().utf8());
+}
+
+static void didFail(WKContextRef, WKDownloadRef wkDownload, WKErrorRef error, const void *clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    if (webkitDownloadIsCancelled(download.get())) {
+        // Cancellation takes precedence over other errors.
+        webkitDownloadCancelled(download.get());
+    } else
+        webkitDownloadFailed(download.get(), toImpl(error)->platformError());
+    webkitWebContextRemoveDownload(wkDownload);
+}
+
+static void didCancel(WKContextRef, WKDownloadRef wkDownload, const void *clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    webkitDownloadCancelled(download.get());
+    webkitWebContextRemoveDownload(wkDownload);
+}
+
+static void didFinish(WKContextRef wkContext, WKDownloadRef wkDownload, const void *clientInfo)
+{
+    GRefPtr<WebKitDownload> download = webkitWebContextGetOrCreateDownload(wkDownload);
+    webkitDownloadFinished(download.get());
+    webkitWebContextRemoveDownload(wkDownload);
+}
+
+void attachDownloadClientToContext(WebKitWebContext* webContext)
+{
+    WKContextDownloadClient wkDownloadClient = {
+        kWKContextDownloadClientCurrentVersion,
+        webContext, // ClientInfo
+        didStart,
+        0, // didReceiveAuthenticationChallenge
+        didReceiveResponse,
+        didReceiveData,
+        0, // shouldDecodeSourceDataOfMIMEType
+        decideDestinationWithSuggestedFilename,
+        didCreateDestination,
+        didFinish,
+        didFail,
+        didCancel,
+        0, // processDidCrash
+    };
+    WKContextSetDownloadClient(webkitWebContextGetWKContext(webContext), &wkDownloadClient);
+}
+
diff --git a/Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.h b/Source/WebKit2/UIProcess/API/gtk/WebKitDownloadClient.h
new file mode 100644 (file)
index 0000000..c2566e8
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef WebKitDownloadClient_h
+#define WebKitDownloadClient_h
+
+#include "WebKitWebContext.h"
+
+void attachDownloadClientToContext(WebKitWebContext*);
+
+#endif
index 6c24aa1..addcf1b 100644 (file)
@@ -57,3 +57,12 @@ COMPILE_ASSERT_MATCHING_ENUM(WEBKIT_PLUGIN_ERROR_CANNOT_LOAD_PLUGIN, PluginError
 COMPILE_ASSERT_MATCHING_ENUM(WEBKIT_PLUGIN_ERROR_JAVA_UNAVAILABLE, PluginErrorJavaUnavailable);
 COMPILE_ASSERT_MATCHING_ENUM(WEBKIT_PLUGIN_ERROR_CONNECTION_CANCELLED, PluginErrorConnectionCancelled);
 COMPILE_ASSERT_MATCHING_ENUM(WEBKIT_PLUGIN_ERROR_WILL_HANDLE_LOAD, PluginErrorWillHandleLoad);
+
+GQuark webkit_download_error_quark()
+{
+    return g_quark_from_static_string(WebCore::errorDomainDownload);
+}
+
+COMPILE_ASSERT_MATCHING_ENUM(WEBKIT_DOWNLOAD_ERROR_NETWORK, DownloadErrorNetwork);
+COMPILE_ASSERT_MATCHING_ENUM(WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER, DownloadErrorCancelledByUser);
+COMPILE_ASSERT_MATCHING_ENUM(WEBKIT_DOWNLOAD_ERROR_DESTINATION, DownloadErrorDestination);
index 3ce0e20..9b0e630 100644 (file)
@@ -32,6 +32,7 @@ G_BEGIN_DECLS
 #define WEBKIT_NETWORK_ERROR webkit_network_error_quark ()
 #define WEBKIT_POLICY_ERROR  webkit_policy_error_quark ()
 #define WEBKIT_PLUGIN_ERROR  webkit_plugin_error_quark ()
+#define WEBKIT_DOWNLOAD_ERROR webkit_download_error_quark ()
 
 /**
  * WebKitNetworkError:
@@ -89,14 +90,31 @@ typedef enum {
     WEBKIT_PLUGIN_ERROR_WILL_HANDLE_LOAD = 204,
 } WebKitPluginError;
 
+/**
+ * WebKitDownloadError:
+ * @WEBKIT_DOWNLOAD_ERROR_NETWORK: Download failure due to network error
+ * @WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER: Download was cancelled by user
+ * @WEBKIT_DOWNLOAD_ERROR_DESTINATION: Download failure due to destination error
+ *
+ * Enum values used to denote the various download errors.
+ */
+typedef enum {
+    WEBKIT_DOWNLOAD_ERROR_NETWORK = 499,
+    WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER = 400,
+    WEBKIT_DOWNLOAD_ERROR_DESTINATION = 401
+} WebKitDownloadError;
+
+WEBKIT_API GQuark
+webkit_network_error_quark  (void);
+
 WEBKIT_API GQuark
-webkit_network_error_quark (void);
+webkit_policy_error_quark   (void);
 
 WEBKIT_API GQuark
-webkit_policy_error_quark  (void);
+webkit_plugin_error_quark   (void);
 
 WEBKIT_API GQuark
-webkit_plugin_error_quark  (void);
+webkit_download_error_quark (void);
 
 G_END_DECLS
 
index ff85bca..360b0a6 100644 (file)
 #include "config.h"
 #include "WebKitWebContext.h"
 
+#include "WebContext.h"
+#include "WebKitDownloadClient.h"
+#include "WebKitDownloadPrivate.h"
 #include "WebKitPrivate.h"
 #include "WebKitWebContextPrivate.h"
+#include <wtf/HashMap.h>
+#include <wtf/gobject/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+using namespace WebKit;
+
+enum {
+    DOWNLOAD_STARTED,
+
+    LAST_SIGNAL
+};
 
 struct _WebKitWebContextPrivate {
     WKRetainPtr<WKContextRef> context;
 };
 
+static guint signals[LAST_SIGNAL] = { 0, };
+
 G_DEFINE_TYPE(WebKitWebContext, webkit_web_context, G_TYPE_OBJECT)
 
 static void webkitWebContextFinalize(GObject* object)
@@ -47,15 +63,32 @@ static void webkit_web_context_class_init(WebKitWebContextClass* webContextClass
     GObjectClass* gObjectClass = G_OBJECT_CLASS(webContextClass);
     gObjectClass->finalize = webkitWebContextFinalize;
 
+    /**
+     * WebKitWebContext::download-started:
+     * @context: the #WebKitWebContext
+     * @download: the #WebKitDownload associated with this event
+     *
+     * This signal is emitted when a new download request is made.
+     */
+    signals[DOWNLOAD_STARTED] =
+        g_signal_new("download-started",
+                     G_TYPE_FROM_CLASS(gObjectClass),
+                     G_SIGNAL_RUN_LAST,
+                     0, 0, 0,
+                     g_cclosure_marshal_VOID__OBJECT,
+                     G_TYPE_NONE, 1,
+                     WEBKIT_TYPE_DOWNLOAD);
+
     g_type_class_add_private(webContextClass, sizeof(WebKitWebContextPrivate));
 }
 
-
 static gpointer createDefaultWebContext(gpointer)
 {
     WebKitWebContext* webContext = WEBKIT_WEB_CONTEXT(g_object_new(WEBKIT_TYPE_WEB_CONTEXT, NULL));
     webContext->priv->context = WKContextGetSharedProcessContext();
     WKContextSetCacheModel(webContext->priv->context.get(), kWKCacheModelPrimaryWebBrowser);
+    attachDownloadClientToContext(webContext);
+
     return webContext;
 }
 
@@ -150,6 +183,59 @@ WebKitCacheModel webkit_web_context_get_cache_model(WebKitWebContext* context)
     return WEBKIT_CACHE_MODEL_WEB_BROWSER;
 }
 
+typedef HashMap<WKDownloadRef, GRefPtr<WebKitDownload> > DownloadsMap;
+
+static DownloadsMap& downloadsMap()
+{
+    DEFINE_STATIC_LOCAL(DownloadsMap, downloads, ());
+    return downloads;
+}
+
+/**
+ * webkit_web_context_download_uri:
+ * @context: a #WebKitWebContext
+ * @uri: the URI to download
+ *
+ * Requests downloading of the specified URI string.
+ *
+ * Returns: (transfer full): a new #WebKitDownload representing the
+ *    the download operation.
+ */
+WebKitDownload* webkit_web_context_download_uri(WebKitWebContext* context, const gchar* uri)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
+    g_return_val_if_fail(uri, 0);
+
+    WebKitWebContextPrivate* priv = context->priv;
+    WKRetainPtr<WKURLRef> wkURL(AdoptWK, WKURLCreateWithUTF8CString(uri));
+    WKRetainPtr<WKURLRequestRef> wkRequest(AdoptWK, WKURLRequestCreateWithWKURL(wkURL.get()));
+    WKRetainPtr<WKDownloadRef> wkDownload = WKContextDownloadURLRequest(priv->context.get(), wkRequest.get());
+    WebKitDownload* download = webkitDownloadCreate(wkDownload.get());
+    downloadsMap().set(wkDownload.get(), download);
+    return download;
+}
+
+WebKitDownload* webkitWebContextGetOrCreateDownload(WKDownloadRef wkDownload)
+{
+    GRefPtr<WebKitDownload> download = downloadsMap().get(wkDownload);
+    if (download)
+        return download.get();
+
+    download = adoptGRef(webkitDownloadCreate(wkDownload));
+    downloadsMap().set(wkDownload, download.get());
+    return download.get();
+}
+
+void webkitWebContextRemoveDownload(WKDownloadRef wkDownload)
+{
+    downloadsMap().remove(wkDownload);
+}
+
+void webkitWebContextDownloadStarted(WebKitWebContext* context, WebKitDownload* download)
+{
+    g_signal_emit(context, signals[DOWNLOAD_STARTED], 0, download);
+}
+
 WKContextRef webkitWebContextGetWKContext(WebKitWebContext* context)
 {
     g_assert(WEBKIT_IS_WEB_CONTEXT(context));
index 2f38dbc..b88bed0 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <glib-object.h>
 #include <webkit2/WebKitDefines.h>
+#include <webkit2/WebKitDownload.h>
 
 G_BEGIN_DECLS
 
@@ -89,6 +90,10 @@ webkit_web_context_set_cache_model (WebKitWebContext *context,
 WEBKIT_API WebKitCacheModel
 webkit_web_context_get_cache_model (WebKitWebContext *context);
 
+WEBKIT_API WebKitDownload *
+webkit_web_context_download_uri    (WebKitWebContext *context,
+                                    const gchar      *uri);
+
 G_END_DECLS
 
 #endif
index 86d7dec..d2dd813 100644 (file)
@@ -32,6 +32,9 @@
 G_BEGIN_DECLS
 
 WKContextRef webkitWebContextGetWKContext(WebKitWebContext*);
+WebKitDownload* webkitWebContextGetOrCreateDownload(WKDownloadRef);
+void webkitWebContextRemoveDownload(WKDownloadRef);
+void webkitWebContextDownloadStarted(WebKitWebContext*, WebKitDownload*);
 
 G_END_DECLS
 
index 637976a..478979b 100644 (file)
@@ -27,6 +27,7 @@ WebKitCacheModel
 webkit_web_context_get_default
 webkit_web_context_get_cache_model
 webkit_web_context_set_cache_model
+webkit_web_context_download_uri
 
 <SUBSECTION Standard>
 WebKitWebContextClass
@@ -315,11 +316,14 @@ webkit_download_get_type
 WEBKIT_NETWORK_ERROR
 WEBKIT_PLUGIN_ERROR
 WEBKIT_POLICY_ERROR
+WEBKIT_DOWNLOAD_ERROR
 WebKitNetworkError
 WebKitPluginError
 WebKitPolicyError
+WebKitDownloadError
 webkit_network_error_quark
 webkit_plugin_error_quark
 webkit_policy_error_quark
+webkit_download_error_quark
 </SECTION>
 
index 1759d04..d7cb2ed 100644 (file)
@@ -3,7 +3,8 @@ TEST_PROGS += \
        Programs/WebKit2APITests/TestWebKitWebView \
        Programs/WebKit2APITests/TestWebKitWebLoaderClient \
        Programs/WebKit2APITests/TestWebKitSettings \
-       Programs/WebKit2APITests/TestBackForwardList
+       Programs/WebKit2APITests/TestBackForwardList \
+       Programs/WebKit2APITests/TestDownloads
 
 noinst_PROGRAMS += $(TEST_PROGS)
 
@@ -15,6 +16,7 @@ endif
 
 webkit2_tests_cppflags = \
        -DWEBKIT_EXEC_PATH=\"${shell pwd}/$(top_builddir)/Programs\" \
+       -DWEBKIT_SRC_DIR=\"${shell pwd}/${srcdir}\" \
        $(javascriptcore_cppflags) \
        -I$(srcdir)/Source/JavaScriptCore \
        -I$(srcdir)/Source \
@@ -95,4 +97,11 @@ Programs_WebKit2APITests_TestWebKitAccessibility_SOURCES = \
 Programs_WebKit2APITests_TestWebKitAccessibility_CPPFLAGS = $(webkit2_tests_cppflags) $(ATSPI2_CFLAGS)
 Programs_WebKit2APITests_TestWebKitAccessibility_LDADD = $(webkit2_tests_ldadd) $(ATSPI2_LIBS)
 Programs_WebKit2APITests_TestWebKitAccessibility_LDFLAGS = $(webkit2_tests_ldflags)
-endif
\ No newline at end of file
+endif
+
+Programs_WebKit2APITests_TestDownloads_SOURCES = \
+        Source/WebKit2/UIProcess/API/gtk/tests/TestDownloads.cpp
+Programs_WebKit2APITests_TestDownloads_CPPFLAGS = $(webkit2_tests_cppflags)
+Programs_WebKit2APITests_TestDownloads_LDADD = $(webkit2_tests_ldadd)
+Programs_WebKit2APITests_TestDownloads_LDFLAGS = $(webkit2_tests_ldflags)
+
diff --git a/Source/WebKit2/UIProcess/API/gtk/tests/TestDownloads.cpp b/Source/WebKit2/UIProcess/API/gtk/tests/TestDownloads.cpp
new file mode 100644 (file)
index 0000000..461cefa
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2012 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "TestMain.h"
+#include "WebKitTestServer.h"
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <libsoup/soup.h>
+#include <string.h>
+#include <webkit2/webkit2.h>
+#include <wtf/Vector.h>
+#include <wtf/gobject/GOwnPtr.h>
+#include <wtf/gobject/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+static char* kTempDirectory;
+
+class DownloadTest: public Test {
+public:
+    MAKE_GLIB_TEST_FIXTURE(DownloadTest);
+
+    enum DownloadEvent {
+        Started,
+        ReceivedResponse,
+        CreatedDestination,
+        ReceivedData,
+        Failed,
+        Finished
+    };
+
+    static void receivedResponseCallback(WebKitDownload* download, GParamSpec*, DownloadTest* test)
+    {
+        g_assert(webkit_download_get_response(download));
+        test->receivedResponse(download);
+    }
+
+    static gboolean createdDestinationCallback(WebKitDownload* download, const gchar* destination, DownloadTest* test)
+    {
+        g_assert(webkit_download_get_destination(download));
+        g_assert_cmpstr(webkit_download_get_destination(download), ==, destination);
+        test->createdDestination(download, destination);
+        return TRUE;
+    }
+
+    static gboolean receivedDataCallback(WebKitDownload* download, guint64 dataLength, DownloadTest* test)
+    {
+        test->receivedData(download, dataLength);
+        return TRUE;
+    }
+
+    static gboolean finishedCallback(WebKitDownload* download, DownloadTest* test)
+    {
+        test->finished(download);
+        return TRUE;
+    }
+
+    static gboolean failedCallback(WebKitDownload* download, GError* error, DownloadTest* test)
+    {
+        g_assert(error);
+        test->failed(download, error);
+        return TRUE;
+    }
+
+    static gboolean decideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, DownloadTest* test)
+    {
+        g_assert(suggestedFilename);
+        test->decideDestination(download, suggestedFilename);
+        return TRUE;
+    }
+
+    static void downloadStartedCallback(WebKitWebContext* context, WebKitDownload* download, DownloadTest* test)
+    {
+        test->started(download);
+        g_signal_connect(download, "notify::response", G_CALLBACK(receivedResponseCallback), test);
+        g_signal_connect(download, "created-destination", G_CALLBACK(createdDestinationCallback), test);
+        g_signal_connect(download, "received-data", G_CALLBACK(receivedDataCallback), test);
+        g_signal_connect(download, "finished", G_CALLBACK(finishedCallback), test);
+        g_signal_connect(download, "failed", G_CALLBACK(failedCallback), test);
+        g_signal_connect(download, "decide-destination", G_CALLBACK(decideDestinationCallback), test);
+    }
+
+    DownloadTest()
+        : m_webContext(webkit_web_context_get_default())
+        , m_mainLoop(g_main_loop_new(0, TRUE))
+        , m_downloadSize(0)
+    {
+        g_signal_connect(m_webContext, "download-started", G_CALLBACK(downloadStartedCallback), this);
+    }
+
+    ~DownloadTest()
+    {
+        g_signal_handlers_disconnect_matched(m_webContext, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
+        g_main_loop_unref(m_mainLoop);
+    }
+
+    virtual void started(WebKitDownload* download)
+    {
+        m_downloadEvents.append(Started);
+    }
+
+    virtual void receivedResponse(WebKitDownload* download)
+    {
+        m_downloadEvents.append(ReceivedResponse);
+    }
+
+    virtual void createdDestination(WebKitDownload* download, const char* destination)
+    {
+        m_downloadEvents.append(CreatedDestination);
+    }
+
+    virtual void receivedData(WebKitDownload* download, guint64 dataLength)
+    {
+        m_downloadSize += dataLength;
+        if (!m_downloadEvents.contains(ReceivedData))
+            m_downloadEvents.append(ReceivedData);
+    }
+
+    virtual void finished(WebKitDownload* download)
+    {
+        m_downloadEvents.append(Finished);
+        g_main_loop_quit(m_mainLoop);
+    }
+
+    virtual void failed(WebKitDownload* download, GError* error)
+    {
+        m_downloadEvents.append(Failed);
+    }
+
+    virtual void decideDestination(WebKitDownload* download, const gchar* suggestedFilename)
+    {
+        GOwnPtr<char> destination(g_build_filename(kTempDirectory, suggestedFilename, NULL));
+        GOwnPtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0));
+        webkit_download_set_destination(download, destinationURI.get());
+    }
+
+    void waitUntilDownloadFinishes()
+    {
+        g_main_loop_run(m_mainLoop);
+    }
+
+    void checkDestinationAndDeleteFile(WebKitDownload* download, const char* expectedName)
+    {
+        if (!webkit_download_get_destination(download))
+            return;
+        GRefPtr<GFile> destFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(download)));
+        GOwnPtr<char> destBasename(g_file_get_basename(destFile.get()));
+        g_assert_cmpstr(destBasename.get(), ==, expectedName);
+
+        g_file_delete(destFile.get(), 0, 0);
+    }
+
+    WebKitWebContext* m_webContext;
+    GMainLoop* m_mainLoop;
+    Vector<DownloadEvent> m_downloadEvents;
+    guint64 m_downloadSize;
+};
+
+static CString getWebKit1TestResoucesDir()
+{
+    GOwnPtr<char> resourcesDir(g_build_filename(WEBKIT_SRC_DIR, "Source", "WebKit", "gtk", "tests", "resources", NULL));
+    return resourcesDir.get();
+}
+
+static void testDownloadLocalFile(DownloadTest* test, gconstpointer)
+{
+    GOwnPtr<char> sourcePath(g_build_filename(getWebKit1TestResoucesDir().data(), "test.pdf", NULL));
+    GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get()));
+    GRefPtr<GFileInfo> sourceInfo = adoptGRef(g_file_query_info(source.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
+    GOwnPtr<char> sourceURI(g_file_get_uri(source.get()));
+    GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(test->m_webContext, sourceURI.get()));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 5);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+    g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination);
+    g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData);
+    g_assert_cmpint(events[4], ==, DownloadTest::Finished);
+
+    g_assert_cmpint(test->m_downloadSize, ==, g_file_info_get_size(sourceInfo.get()));
+    g_assert(webkit_download_get_destination(download.get()));
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1);
+    test->checkDestinationAndDeleteFile(download.get(), "test.pdf");
+}
+
+class DownloadErrorTest: public DownloadTest {
+public:
+    MAKE_GLIB_TEST_FIXTURE(DownloadErrorTest);
+
+    DownloadErrorTest()
+        : m_expectedError(WEBKIT_DOWNLOAD_ERROR_NETWORK)
+    {
+    }
+
+    void receivedResponse(WebKitDownload* download)
+    {
+        DownloadTest::receivedResponse(download);
+        if (m_expectedError == WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER)
+            webkit_download_cancel(download);
+    }
+
+    void createdDestination(WebKitDownload* download, const char* destination)
+    {
+        g_assert_not_reached();
+    }
+
+    void failed(WebKitDownload* download, GError* error)
+    {
+        g_assert(g_error_matches(error, WEBKIT_DOWNLOAD_ERROR, m_expectedError));
+        DownloadTest::failed(download, error);
+    }
+
+    void decideDestination(WebKitDownload* download, const gchar* suggestedFilename)
+    {
+        if (m_expectedError != WEBKIT_DOWNLOAD_ERROR_DESTINATION) {
+            DownloadTest::decideDestination(download, suggestedFilename);
+            return;
+        }
+        webkit_download_set_destination(download, "file:///foo/bar");
+    }
+
+    WebKitDownloadError m_expectedError;
+};
+
+static void testDownloadLocalFileError(DownloadErrorTest* test, gconstpointer)
+{
+    test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_NETWORK;
+    GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(test->m_webContext, "file:///foo/bar"));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 3);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::Failed);
+    g_assert_cmpint(events[2], ==, DownloadTest::Finished);
+    events.clear();
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+
+    test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_DESTINATION;
+    GOwnPtr<char> path(g_build_filename(getWebKit1TestResoucesDir().data(), "test.pdf", NULL));
+    GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get()));
+    GOwnPtr<char> uri(g_file_get_uri(file.get()));
+    download = adoptGRef(webkit_web_context_download_uri(test->m_webContext, uri.get()));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 4);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+    g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+    g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+    events.clear();
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+    test->checkDestinationAndDeleteFile(download.get(), "bar");
+
+    test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER;
+    download = adoptGRef(webkit_web_context_download_uri(test->m_webContext, uri.get()));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 4);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+    g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+    g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+    events.clear();
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+    test->checkDestinationAndDeleteFile(download.get(), "test.pdf");
+}
+
+static WebKitTestServer* kServer;
+static const char* kServerSuggestedFilename = "webkit-downloaded-file";
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+    if (message->method != SOUP_METHOD_GET) {
+        soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+        return;
+    }
+
+    GOwnPtr<char> filePath(g_build_filename(getWebKit1TestResoucesDir().data(), path, NULL));
+    char* contents;
+    gsize contentsLength;
+    if (!g_file_get_contents(filePath.get(), &contents, &contentsLength, 0)) {
+        soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+        soup_message_body_complete(message->response_body);
+        return;
+    }
+
+    soup_message_set_status(message, SOUP_STATUS_OK);
+
+    GOwnPtr<char> contentDisposition(g_strdup_printf("filename=%s", kServerSuggestedFilename));
+    soup_message_headers_append(message->response_headers, "Content-Disposition", contentDisposition.get());
+    soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
+
+    soup_message_body_complete(message->response_body);
+}
+
+static void testDownloadRemoteFile(DownloadTest* test, gconstpointer)
+{
+    GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(test->m_webContext, kServer->getURIForPath("/test.pdf").data()));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 5);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+    g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination);
+    g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData);
+    g_assert_cmpint(events[4], ==, DownloadTest::Finished);
+    events.clear();
+
+    g_assert(webkit_download_get_destination(download.get()));
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1);
+    test->checkDestinationAndDeleteFile(download.get(), kServerSuggestedFilename);
+}
+
+static void testDownloadRemoteFileError(DownloadErrorTest* test, gconstpointer)
+{
+    test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_NETWORK;
+    GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(test->m_webContext,
+                                                                                 kServer->getURIForPath("/foo").data()));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 4);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+    g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+    g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+    events.clear();
+    WebKitURIResponse* response = webkit_download_get_response(download.get());
+    g_assert_cmpuint(webkit_uri_response_get_status_code(response), ==, 404);
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+
+    test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_DESTINATION;
+    download = adoptGRef(webkit_web_context_download_uri(test->m_webContext, kServer->getURIForPath("/test.pdf").data()));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 4);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+    g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+    g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+    events.clear();
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+    test->checkDestinationAndDeleteFile(download.get(), "bar");
+
+    test->m_expectedError = WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER;
+    download = adoptGRef(webkit_web_context_download_uri(test->m_webContext, kServer->getURIForPath("/test.pdf").data()));
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
+    test->waitUntilDownloadFinishes();
+
+    events = test->m_downloadEvents;
+    g_assert_cmpint(events.size(), ==, 4);
+    g_assert_cmpint(events[0], ==, DownloadTest::Started);
+    g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
+    g_assert_cmpint(events[2], ==, DownloadTest::Failed);
+    g_assert_cmpint(events[3], ==, DownloadTest::Finished);
+    events.clear();
+    g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
+    test->checkDestinationAndDeleteFile(download.get(), kServerSuggestedFilename);
+}
+
+void beforeAll()
+{
+    kServer = new WebKitTestServer();
+    kServer->run(serverCallback);
+
+    kTempDirectory = g_dir_make_tmp("WebKit2Tests-XXXXXX", 0);
+    g_assert(kTempDirectory);
+
+    DownloadTest::add("Downloads", "local-file", testDownloadLocalFile);
+    DownloadErrorTest::add("Downloads", "local-file-error", testDownloadLocalFileError);
+    DownloadTest::add("Downloads", "remote-file", testDownloadRemoteFile);
+    DownloadErrorTest::add("Downloads", "remote-file-error", testDownloadRemoteFileError);
+}
+
+void afterAll()
+{
+    delete kServer;
+    g_rmdir(kTempDirectory);
+}
index 224f9f1..e076809 100644 (file)
@@ -1,3 +1,13 @@
+2012-01-24  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK] Implement DownloadClient in WebKit2 GTK+ API
+        https://bugs.webkit.org/show_bug.cgi?id=72952
+
+        Reviewed by Martin Robinson.
+
+        * gtk/generate-gtkdoc:
+        (get_webkit2_options): Ignore WebKitDownloadClient.
+
 2012-01-23  Zan Dobersek  <zandobersek@gmail.com>
 
         [GTK] editing/deleting/5408255.html results are incorrect
index 0c18a38..317487b 100755 (executable)
@@ -60,6 +60,7 @@ def get_webkit2_options():
                    ' -I' + src_path(),
         'ignored_files': glob.glob(src_path('*Private.h')) + \
                          glob.glob(src_path('PageClientImpl.*')) + \
+                         glob.glob(src_path('WebKitDownloadClient.*')) + \
                          glob.glob(src_path('WebKitUIClient.*')) + \
                          glob.glob(src_path('WebKitWebLoaderClient.*')) + \
                          glob.glob(src_path('WebKitWebViewBaseAccessible.*')) + \