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()