Title: [125267] trunk/Source/WebKit2
Revision
125267
Author
[email protected]
Date
2012-08-10 02:51:34 -0700 (Fri, 10 Aug 2012)

Log Message

[WK2][GTK] Implement new API to save a web page using MHTML
https://bugs.webkit.org/show_bug.cgi?id=89873

Reviewed by Carlos Garcia Campos.

Implemented new asynchronous API in WebKitWebView for saving a web
page to a GInputStream or to a file, using MHTML as the only
supported method at the moment.

* UIProcess/API/gtk/WebKitWebView.cpp:
(ViewSaveAsyncData):
(webViewFileSavedCallback):
(webViewMHTMLDataGotCallback):
(webkit_web_view_save):
(webkit_web_view_save_finish):
(webkit_web_view_save_to_file):
(webkit_web_view_save_to_file_finish):
* UIProcess/API/gtk/WebKitWebView.h:

Added new unit test.

* UIProcess/API/gtk/tests/TestWebKitWebView.cpp:
(testWebViewSave):
(beforeAll):
(afterAll):

Updated documentation files with new symbols.

* UIProcess/API/gtk/docs/webkit2gtk-sections.txt:

Modified Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (125266 => 125267)


--- trunk/Source/WebKit2/ChangeLog	2012-08-10 09:46:21 UTC (rev 125266)
+++ trunk/Source/WebKit2/ChangeLog	2012-08-10 09:51:34 UTC (rev 125267)
@@ -1,3 +1,35 @@
+2012-08-10  Mario Sanchez Prada  <[email protected]>
+
+        [WK2][GTK] Implement new API to save a web page using MHTML
+        https://bugs.webkit.org/show_bug.cgi?id=89873
+
+        Reviewed by Carlos Garcia Campos.
+
+        Implemented new asynchronous API in WebKitWebView for saving a web
+        page to a GInputStream or to a file, using MHTML as the only
+        supported method at the moment.
+
+        * UIProcess/API/gtk/WebKitWebView.cpp:
+        (ViewSaveAsyncData):
+        (webViewFileSavedCallback):
+        (webViewMHTMLDataGotCallback):
+        (webkit_web_view_save):
+        (webkit_web_view_save_finish):
+        (webkit_web_view_save_to_file):
+        (webkit_web_view_save_to_file_finish):
+        * UIProcess/API/gtk/WebKitWebView.h:
+
+        Added new unit test.
+
+        * UIProcess/API/gtk/tests/TestWebKitWebView.cpp:
+        (testWebViewSave):
+        (beforeAll):
+        (afterAll):
+
+        Updated documentation files with new symbols.
+
+        * UIProcess/API/gtk/docs/webkit2gtk-sections.txt:
+
 2012-08-09  Carlos Garcia Campos  <[email protected]>
 
         Handle SSL errors for SOUP

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp (125266 => 125267)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp	2012-08-10 09:46:21 UTC (rev 125266)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.cpp	2012-08-10 09:51:34 UTC (rev 125267)
@@ -2400,3 +2400,171 @@
     WebPageProxy* page = webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(webView));
     return page->canShowMIMEType(String::fromUTF8(mimeType));
 }
+
+struct ViewSaveAsyncData {
+    WKRetainPtr<WKDataRef> wkData;
+    GRefPtr<GFile> file;
+    GRefPtr<GCancellable> cancellable;
+};
+WEBKIT_DEFINE_ASYNC_DATA_STRUCT(ViewSaveAsyncData)
+
+static void fileReplaceContentsCallback(GObject* object, GAsyncResult* result, gpointer data)
+{
+    GFile* file = G_FILE(object);
+    GRefPtr<GSimpleAsyncResult> savedToFileResult = adoptGRef(G_SIMPLE_ASYNC_RESULT(data));
+
+    GError* error = 0;
+    if (!g_file_replace_contents_finish(file, result, 0, &error))
+        g_simple_async_result_take_error(savedToFileResult.get(), error);
+
+    g_simple_async_result_complete(savedToFileResult.get());
+}
+
+static void getContentsAsMHTMLDataCallback(WKDataRef wkData, WKErrorRef, void* context)
+{
+    GRefPtr<GSimpleAsyncResult> result = adoptGRef(G_SIMPLE_ASYNC_RESULT(context));
+    ViewSaveAsyncData* data = ""
+    GError* error = 0;
+
+    if (g_cancellable_set_error_if_cancelled(data->cancellable.get(), &error))
+        g_simple_async_result_take_error(result.get(), error);
+    else {
+        // We need to retain the data until the asyncronous process
+        // initiated by the user has finished completely.
+        data->wkData = wkData;
+
+        // If we are saving to a file we need to write the data on disk before finishing.
+        if (g_simple_async_result_get_source_tag(result.get()) == webkit_web_view_save_to_file) {
+            ASSERT(G_IS_FILE(data->file.get()));
+            g_file_replace_contents_async(data->file.get(), reinterpret_cast<const gchar*>(WKDataGetBytes(data->wkData.get())), WKDataGetSize(data->wkData.get()), 0, FALSE, G_FILE_CREATE_REPLACE_DESTINATION,
+                                          data->cancellable.get(), fileReplaceContentsCallback, g_object_ref(result.get()));
+            return;
+        }
+    }
+
+    g_simple_async_result_complete(result.get());
+}
+
+/**
+ * webkit_web_view_save:
+ * @web_view: a #WebKitWebView
+ * @save_mode: the #WebKitSaveMode specifying how the web page should be saved.
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously save the current web page associated to the
+ * #WebKitWebView into a self-contained format using the mode
+ * specified in @save_mode.
+ *
+ * When the operation is finished, @callback will be called. You can
+ * then call webkit_web_view_save_finish() to get the result of the
+ * operation.
+ */
+void webkit_web_view_save(WebKitWebView* webView, WebKitSaveMode saveMode, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
+
+    // We only support MHTML at the moment.
+    g_return_if_fail(saveMode == WEBKIT_SAVE_MODE_MHTML);
+
+    GSimpleAsyncResult* result = g_simple_async_result_new(G_OBJECT(webView), callback, userData,
+                                                           reinterpret_cast<gpointer>(webkit_web_view_save));
+    ViewSaveAsyncData* data = ""
+    data->cancellable = cancellable;
+    g_simple_async_result_set_op_res_gpointer(result, data, reinterpret_cast<GDestroyNotify>(destroyViewSaveAsyncData));
+
+    WKPageRef wkPage = toAPI(webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(webView)));
+    WKPageGetContentsAsMHTMLData(wkPage, false, result, getContentsAsMHTMLDataCallback);
+}
+
+/**
+ * webkit_web_view_save_finish:
+ * @web_view: a #WebKitWebView
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finish an asynchronous operation started with webkit_web_view_save().
+ *
+ * Returns: (transfer full): a #GInputStream with the result of saving
+ *    the current web page or %NULL in case of error.
+ */
+GInputStream* webkit_web_view_save_finish(WebKitWebView* webView, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), 0);
+    g_return_val_if_fail(G_IS_ASYNC_RESULT(result), 0);
+
+    GSimpleAsyncResult* simple = G_SIMPLE_ASYNC_RESULT(result);
+    g_warn_if_fail(g_simple_async_result_get_source_tag(simple) == webkit_web_view_save);
+
+    if (g_simple_async_result_propagate_error(simple, error))
+        return 0;
+
+    GInputStream* dataStream = g_memory_input_stream_new();
+    ViewSaveAsyncData* data = ""
+    gsize length = WKDataGetSize(data->wkData.get());
+    if (length)
+        g_memory_input_stream_add_data(G_MEMORY_INPUT_STREAM(dataStream), g_memdup(WKDataGetBytes(data->wkData.get()), length), length, g_free);
+
+    return dataStream;
+}
+
+/**
+ * webkit_web_view_save_to_file:
+ * @web_view: a #WebKitWebView
+ * @file: the #GFile where the current web page should be saved to.
+ * @save_mode: the #WebKitSaveMode specifying how the web page should be saved.
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously save the current web page associated to the
+ * #WebKitWebView into a self-contained format using the mode
+ * specified in @save_mode and writing it to @file.
+ *
+ * When the operation is finished, @callback will be called. You can
+ * then call webkit_web_view_save_to_file_finish() to get the result of the
+ * operation.
+ */
+void webkit_web_view_save_to_file(WebKitWebView* webView, GFile* file, WebKitSaveMode saveMode, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEB_VIEW(webView));
+    g_return_if_fail(G_IS_FILE(file));
+
+    // We only support MHTML at the moment.
+    g_return_if_fail(saveMode == WEBKIT_SAVE_MODE_MHTML);
+
+    GSimpleAsyncResult* result = g_simple_async_result_new(G_OBJECT(webView), callback, userData,
+                                                           reinterpret_cast<gpointer>(webkit_web_view_save_to_file));
+    ViewSaveAsyncData* data = ""
+    data->file = file;
+    data->cancellable = cancellable;
+    g_simple_async_result_set_op_res_gpointer(result, data, reinterpret_cast<GDestroyNotify>(destroyViewSaveAsyncData));
+
+    WKPageRef wkPage = toAPI(webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(webView)));
+    WKPageGetContentsAsMHTMLData(wkPage, false, result, getContentsAsMHTMLDataCallback);
+}
+
+/**
+ * webkit_web_view_save_to_file_finish:
+ * @web_view: a #WebKitWebView
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finish an asynchronous operation started with webkit_web_view_save_to_file().
+ *
+ * Returns: %TRUE if the web page was successfully saved to a file or %FALSE otherwise.
+ */
+gboolean webkit_web_view_save_to_file_finish(WebKitWebView* webView, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEB_VIEW(webView), FALSE);
+    g_return_val_if_fail(G_IS_ASYNC_RESULT(result), FALSE);
+
+    GSimpleAsyncResult* simple = G_SIMPLE_ASYNC_RESULT(result);
+    g_warn_if_fail(g_simple_async_result_get_source_tag(simple) == webkit_web_view_save_to_file);
+
+    if (g_simple_async_result_propagate_error(simple, error))
+        return FALSE;
+
+    return TRUE;
+}

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h (125266 => 125267)


--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h	2012-08-10 09:46:21 UTC (rev 125266)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebView.h	2012-08-10 09:51:34 UTC (rev 125267)
@@ -118,6 +118,17 @@
     WEBKIT_LOAD_FINISHED
 } WebKitLoadEvent;
 
+/**
+ * WebKitSaveMode:
+ * @WEBKIT_SAVE_MODE_MHTML: Save the current page using the MHTML format.
+ *
+ * Enum values to specify the different ways in which a #WebKitWebView
+ * can save its current web page into a self-contained file.
+ */
+typedef enum {
+    WEBKIT_SAVE_MODE_MHTML
+} WebKitSaveMode;
+
 struct _WebKitWebView {
     WebKitWebViewBase parent;
 
@@ -319,6 +330,31 @@
 webkit_web_view_can_show_mime_type                 (WebKitWebView             *web_view,
                                                     const gchar               *mime_type);
 
+WEBKIT_API void
+webkit_web_view_save                               (WebKitWebView             *web_view,
+                                                    WebKitSaveMode             save_mode,
+                                                    GCancellable              *cancellable,
+                                                    GAsyncReadyCallback        callback,
+                                                    gpointer                   user_data);
+
+WEBKIT_API GInputStream *
+webkit_web_view_save_finish                        (WebKitWebView             *web_view,
+                                                    GAsyncResult              *result,
+                                                    GError                   **error);
+
+WEBKIT_API void
+webkit_web_view_save_to_file                       (WebKitWebView             *web_view,
+                                                    GFile                     *file,
+                                                    WebKitSaveMode             save_mode,
+                                                    GCancellable              *cancellable,
+                                                    GAsyncReadyCallback        callback,
+                                                    gpointer                   user_data);
+
+WEBKIT_API gboolean
+webkit_web_view_save_to_file_finish                (WebKitWebView             *web_view,
+                                                    GAsyncResult              *result,
+                                                    GError                   **error);
+
 G_END_DECLS
 
 #endif

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-sections.txt (125266 => 125267)


--- trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-sections.txt	2012-08-10 09:46:21 UTC (rev 125266)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-sections.txt	2012-08-10 09:51:34 UTC (rev 125267)
@@ -62,6 +62,7 @@
 WebKitWebView
 WebKitLoadEvent
 WebKitPolicyDecisionType
+WebKitSaveMode
 
 <SUBSECTION Editing Commands>
 WEBKIT_EDITING_COMMAND_CUT
@@ -105,6 +106,10 @@
 webkit_web_view_run_javascript
 webkit_web_view_run_javascript_finish
 webkit_web_view_can_show_mime_type
+webkit_web_view_save
+webkit_web_view_save_finish
+webkit_web_view_save_to_file
+webkit_web_view_save_to_file_finish
 
 <SUBSECTION WebKitJavascriptResult>
 WebKitJavascriptResult

Modified: trunk/Source/WebKit2/UIProcess/API/gtk/tests/TestWebKitWebView.cpp (125266 => 125267)


--- trunk/Source/WebKit2/UIProcess/API/gtk/tests/TestWebKitWebView.cpp	2012-08-10 09:46:21 UTC (rev 125266)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/tests/TestWebKitWebView.cpp	2012-08-10 09:51:34 UTC (rev 125267)
@@ -21,6 +21,7 @@
 #include "WebViewTest.h"
 #include <_javascript_Core/JSStringRef.h>
 #include <_javascript_Core/JSValueRef.h>
+#include <glib/gstdio.h>
 #include <wtf/HashSet.h>
 #include <wtf/gobject/GRefPtr.h>
 #include <wtf/text/StringHash.h>
@@ -946,6 +947,102 @@
     g_assert_cmpstr(static_cast<char*>(g_hash_table_lookup(values, "password")), ==, "secret");
 }
 
+class SaveWebViewTest: public WebViewTest {
+public:
+    MAKE_GLIB_TEST_FIXTURE(SaveWebViewTest);
+
+    SaveWebViewTest()
+        : m_tempDirectory(g_dir_make_tmp("WebKit2SaveViewTest-XXXXXX", 0))
+    {
+    }
+
+    ~SaveWebViewTest()
+    {
+        if (G_IS_FILE(m_file.get()))
+            g_file_delete(m_file.get(), 0, 0);
+
+        if (G_IS_INPUT_STREAM(m_inputStream.get()))
+            g_input_stream_close(m_inputStream.get(), 0, 0);
+
+        if (m_tempDirectory)
+            g_rmdir(m_tempDirectory.get());
+    }
+
+    static void webViewSavedToStreamCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test)
+    {
+        GOwnPtr<GError> error;
+        test->m_inputStream = adoptGRef(webkit_web_view_save_finish(test->m_webView, result, &error.outPtr()));
+        g_assert(G_IS_INPUT_STREAM(test->m_inputStream.get()));
+        g_assert(!error);
+
+        test->quitMainLoop();
+    }
+
+    static void webViewSavedToFileCallback(GObject* object, GAsyncResult* result, SaveWebViewTest* test)
+    {
+        GOwnPtr<GError> error;
+        g_assert(webkit_web_view_save_to_file_finish(test->m_webView, result, &error.outPtr()));
+        g_assert(!error);
+
+        test->quitMainLoop();
+    }
+
+    void saveAndWaitForStream()
+    {
+        webkit_web_view_save(m_webView, WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToStreamCallback), this);
+        g_main_loop_run(m_mainLoop);
+    }
+
+    void saveAndWaitForFile()
+    {
+        m_saveDestinationFilePath.set(g_build_filename(m_tempDirectory.get(), "testWebViewSaveResult.mht", NULL));
+        m_file = adoptGRef(g_file_new_for_path(m_saveDestinationFilePath.get()));
+        webkit_web_view_save_to_file(m_webView, m_file.get(), WEBKIT_SAVE_MODE_MHTML, 0, reinterpret_cast<GAsyncReadyCallback>(webViewSavedToFileCallback), this);
+        g_main_loop_run(m_mainLoop);
+    }
+
+    GOwnPtr<char> m_tempDirectory;
+    GOwnPtr<char> m_saveDestinationFilePath;
+    GRefPtr<GInputStream> m_inputStream;
+    GRefPtr<GFile> m_file;
+};
+
+static void testWebViewSave(SaveWebViewTest* test, gconstpointer)
+{
+    test->loadHtml("<html>"
+                   "<body>"
+                   "  <p>A paragraph with plain text</p>"
+                   "  <p>"
+                   "    A red box: <img src=''></br>"
+                   "    A blue box: <img src=''>"
+                   "  </p>"
+                   "</body>"
+                   "</html>", 0);
+    test->waitUntilLoadFinished();
+
+    // Write to a file and to an input stream.
+    test->saveAndWaitForFile();
+    test->saveAndWaitForStream();
+
+    // We should have exactly the same amount of bytes in the file
+    // than those coming from the GInputStream. We don't compare the
+    // strings read since the 'Date' field and the boundaries will be
+    // different on each case. MHTML functionality will be tested by
+    // Layout tests, so checking the amount of bytes is enough.
+    GOwnPtr<GError> error;
+    gchar buffer[512] = { 0 };
+    gssize readBytes = 0;
+    gssize totalBytesFromStream = 0;
+    while (readBytes = g_input_stream_read(test->m_inputStream.get(), &buffer, 512, 0, &error.outPtr())) {
+        g_assert(!error);
+        totalBytesFromStream += readBytes;
+    }
+
+    // Check that the file exists and that it contains the same amount of bytes.
+    GRefPtr<GFileInfo> fileInfo = adoptGRef(g_file_query_info(test->m_file.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
+    g_assert_cmpint(g_file_info_get_size(fileInfo.get()), ==, totalBytesFromStream);
+}
+
 void beforeAll()
 {
     WebViewTest::add("WebKitWebView", "default-context", testWebViewDefaultContext);
@@ -965,6 +1062,7 @@
     FullScreenClientTest::add("WebKitWebView", "fullscreen", testWebViewFullScreen);
     WebViewTest::add("WebKitWebView", "can-show-mime-type", testWebViewCanShowMIMEType);
     FormClientTest::add("WebKitWebView", "submit-form", testWebViewSubmitForm);
+    SaveWebViewTest::add("WebKitWebView", "save", testWebViewSave);
 }
 
 void afterAll()
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to