| /* |
| * Copyright (C) 2012, 2014 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 "WebKitTestServer.h" |
| #include "WebViewTest.h" |
| #include <WebCore/SoupVersioning.h> |
| #include <glib/gstdio.h> |
| #include <libsoup/soup.h> |
| #include <string.h> |
| #include <wtf/Vector.h> |
| #include <wtf/glib/GRefPtr.h> |
| #include <wtf/glib/GUniquePtr.h> |
| #include <wtf/text/CString.h> |
| |
| 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_nonnull(webkit_download_get_response(download)); |
| test->receivedResponse(download); |
| } |
| |
| static void createdDestinationCallback(WebKitDownload* download, const gchar* destination, DownloadTest* test) |
| { |
| g_assert_nonnull(webkit_download_get_destination(download)); |
| g_assert_cmpstr(webkit_download_get_destination(download), ==, destination); |
| GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destination)); |
| g_assert_true(g_file_query_exists(file.get(), nullptr)); |
| test->createdDestination(download, destination); |
| } |
| |
| static void receivedDataCallback(WebKitDownload* download, guint64 dataLength, DownloadTest* test) |
| { |
| test->receivedData(download, dataLength); |
| } |
| |
| static void finishedCallback(WebKitDownload* download, DownloadTest* test) |
| { |
| test->finished(download); |
| } |
| |
| static void failedCallback(WebKitDownload* download, GError* error, DownloadTest* test) |
| { |
| g_assert_nonnull(error); |
| |
| const char* destinationURI = webkit_download_get_destination(download); |
| if (destinationURI) { |
| GUniquePtr<char> tempFileURI(g_strconcat(destinationURI, ".wkdownload", nullptr)); |
| GRefPtr<GFile> tempFile = adoptGRef(g_file_new_for_uri(tempFileURI.get())); |
| g_assert_false(g_file_query_exists(tempFile.get(), nullptr)); |
| } |
| |
| test->failed(download, error); |
| } |
| |
| static gboolean decideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, DownloadTest* test) |
| { |
| g_assert_nonnull(suggestedFilename); |
| test->decideDestination(download, suggestedFilename); |
| return TRUE; |
| } |
| |
| static void downloadStartedCallback(WebKitWebContext* context, WebKitDownload* download, DownloadTest* test) |
| { |
| g_assert_nonnull(webkit_download_get_request(download)); |
| 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_mainLoop(g_main_loop_new(nullptr, TRUE)) |
| , m_downloadSize(0) |
| , m_allowOverwrite(false) |
| { |
| g_signal_connect(m_webContext.get(), "download-started", G_CALLBACK(downloadStartedCallback), this); |
| } |
| |
| ~DownloadTest() |
| { |
| g_signal_handlers_disconnect_matched(m_webContext.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); |
| g_main_loop_unref(m_mainLoop); |
| } |
| |
| virtual void started(WebKitDownload* download) |
| { |
| m_downloadSize = 0; |
| m_downloadEvents.clear(); |
| 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) |
| { |
| g_assert_cmpuint(m_downloadSize, ==, webkit_download_get_received_data_length(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) |
| { |
| GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr)); |
| GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0)); |
| webkit_download_set_destination(download, destinationURI.get()); |
| } |
| |
| GRefPtr<WebKitDownload> downloadURIAndWaitUntilFinishes(const CString& requestURI) |
| { |
| GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(m_webContext.get(), requestURI.data())); |
| assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get())); |
| |
| g_assert_false(webkit_download_get_allow_overwrite(download.get())); |
| webkit_download_set_allow_overwrite(download.get(), m_allowOverwrite); |
| g_assert_cmpint(webkit_download_get_allow_overwrite(download.get()), ==, m_allowOverwrite); |
| |
| WebKitURIRequest* request = webkit_download_get_request(download.get()); |
| g_assert_nonnull(request); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI); |
| |
| g_main_loop_run(m_mainLoop); |
| |
| return download; |
| } |
| |
| 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))); |
| GUniquePtr<char> destBasename(g_file_get_basename(destFile.get())); |
| g_assert_cmpstr(destBasename.get(), ==, expectedName); |
| |
| g_file_delete(destFile.get(), 0, 0); |
| } |
| |
| GMainLoop* m_mainLoop; |
| Vector<DownloadEvent> m_downloadEvents; |
| guint64 m_downloadSize; |
| bool m_allowOverwrite; |
| }; |
| |
| static GRefPtr<WebKitDownload> downloadLocalFileSuccessfully(DownloadTest* test, const char* filename) |
| { |
| GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr)); |
| 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)); |
| GUniquePtr<char> sourceURI(g_file_get_uri(source.get())); |
| GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(sourceURI.get()); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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); |
| |
| WebKitURIRequest* request = webkit_download_get_request(download.get()); |
| g_assert_nonnull(request); |
| g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, sourceURI.get()); |
| |
| g_assert_cmpint(test->m_downloadSize, ==, g_file_info_get_size(sourceInfo.get())); |
| g_assert_nonnull(webkit_download_get_destination(download.get())); |
| g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1); |
| |
| return download; |
| } |
| |
| static void testDownloadLocalFile(DownloadTest* test, gconstpointer) |
| { |
| static const char* filename = "test.pdf"; |
| GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename); |
| test->checkDestinationAndDeleteFile(download.get(), filename); |
| } |
| |
| static void createFileAtDestination(const char* filename) |
| { |
| GUniquePtr<char> path(g_build_filename(Test::dataDirectory(), filename, nullptr)); |
| GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get())); |
| GUniqueOutPtr<GError> error; |
| g_file_create(file.get(), G_FILE_CREATE_NONE, nullptr, &error.outPtr()); |
| g_assert_no_error(error.get()); |
| g_assert_true(g_file_query_exists(file.get(), nullptr)); |
| } |
| |
| static void testDownloadOverwriteDestinationAllowed(DownloadTest* test, gconstpointer) |
| { |
| static const char* filename = "test.pdf"; |
| createFileAtDestination(filename); |
| |
| test->m_allowOverwrite = true; |
| GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename); |
| test->checkDestinationAndDeleteFile(download.get(), filename); |
| } |
| |
| class DownloadErrorTest: public DownloadTest { |
| public: |
| MAKE_GLIB_TEST_FIXTURE(DownloadErrorTest); |
| |
| enum ExpectedError { |
| NetworkError, |
| DownloadCancelled, |
| InvalidDestination, |
| DestinationExists |
| }; |
| |
| DownloadErrorTest() |
| : m_expectedError(NetworkError) |
| { |
| } |
| |
| void receivedResponse(WebKitDownload* download) |
| { |
| DownloadTest::receivedResponse(download); |
| } |
| |
| void createdDestination(WebKitDownload* download, const char* destination) |
| { |
| if (m_expectedError == DownloadCancelled) |
| webkit_download_cancel(download); |
| else |
| g_assert_not_reached(); |
| } |
| |
| void failed(WebKitDownload* download, GError* error) |
| { |
| g_assert_error(error, WEBKIT_DOWNLOAD_ERROR, expectedErrorToWebKitDownloadError(m_expectedError)); |
| DownloadTest::failed(download, error); |
| } |
| |
| void decideDestination(WebKitDownload* download, const gchar* suggestedFilename) |
| { |
| if (m_expectedError != InvalidDestination) { |
| DownloadTest::decideDestination(download, suggestedFilename); |
| return; |
| } |
| webkit_download_set_destination(download, "file:///foo/bar"); |
| } |
| |
| static WebKitDownloadError expectedErrorToWebKitDownloadError(ExpectedError expected) |
| { |
| switch (expected) { |
| case NetworkError: |
| return WEBKIT_DOWNLOAD_ERROR_NETWORK; |
| case DownloadCancelled: |
| return WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER; |
| case InvalidDestination: |
| return WEBKIT_DOWNLOAD_ERROR_DESTINATION; |
| case DestinationExists: |
| return WEBKIT_DOWNLOAD_ERROR_DESTINATION; |
| default: |
| g_assert_not_reached(); |
| } |
| } |
| |
| ExpectedError m_expectedError; |
| }; |
| |
| static void testDownloadOverwriteDestinationDisallowed(DownloadErrorTest* test, gconstpointer) |
| { |
| static const char* filename = "test.pdf"; |
| createFileAtDestination(filename); |
| |
| test->m_expectedError = DownloadErrorTest::DestinationExists; |
| GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr)); |
| GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get())); |
| GUniquePtr<char> sourceURI(g_file_get_uri(source.get())); |
| GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(sourceURI.get()); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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); |
| g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 0); |
| |
| test->checkDestinationAndDeleteFile(download.get(), filename); |
| } |
| |
| static void testDownloadLocalFileError(DownloadErrorTest* test, gconstpointer) |
| { |
| test->m_expectedError = DownloadErrorTest::NetworkError; |
| GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes("file:///foo/bar"); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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 = DownloadErrorTest::InvalidDestination; |
| GUniquePtr<char> path(g_build_filename(Test::getResourcesDir().data(), "test.pdf", nullptr)); |
| GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get())); |
| GUniquePtr<char> uri(g_file_get_uri(file.get())); |
| download = test->downloadURIAndWaitUntilFinishes(uri.get()); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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 = DownloadErrorTest::DownloadCancelled; |
| download = test->downloadURIAndWaitUntilFinishes(uri.get()); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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 HashMap<CString, CString> s_userAgentMap; |
| |
| #if USE(SOUP2) |
| static void addContentDispositionHTTPHeaderToResponse(SoupMessage* message) |
| #else |
| static void addContentDispositionHTTPHeaderToResponse(SoupServerMessage* message) |
| #endif |
| { |
| GUniquePtr<char> contentDisposition(g_strdup_printf("attachment; filename=%s", kServerSuggestedFilename)); |
| soup_message_headers_append(soup_server_message_get_response_headers(message), "Content-Disposition", contentDisposition.get()); |
| } |
| |
| #if USE(SOUP2) |
| static void writeNextChunk(SoupMessage* message) |
| #else |
| static void writeNextChunk(SoupServerMessage* message) |
| #endif |
| { |
| /* We need a big enough chunk for the sniffer to not block the load */ |
| static const char* chunk = "Testing!Testing!Testing!Testing!Testing!Testing!Testing!" |
| "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" |
| "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" |
| "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" |
| "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" |
| "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!" |
| "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"; |
| soup_message_body_append(soup_server_message_get_response_body(message), SOUP_MEMORY_STATIC, chunk, strlen(chunk)); |
| } |
| |
| #if USE(SOUP2) |
| static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer) |
| #else |
| static void serverCallback(SoupServer* server, SoupServerMessage* message, const char* path, GHashTable*, gpointer) |
| #endif |
| { |
| if (soup_server_message_get_method(message) != SOUP_METHOD_GET) { |
| soup_server_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED, nullptr); |
| return; |
| } |
| |
| auto* responseHeaders = soup_server_message_get_response_headers(message); |
| auto* responseBody = soup_server_message_get_response_body(message); |
| soup_server_message_set_status(message, SOUP_STATUS_OK, nullptr); |
| |
| if (g_str_has_prefix(path, "/ua-")) |
| s_userAgentMap.add(path, soup_message_headers_get_one(soup_server_message_get_request_headers(message), "User-Agent")); |
| |
| if (g_str_equal(path, "/cancel-after-destination")) { |
| // Use an infinite message to make sure it's cancelled before it finishes. |
| soup_message_headers_set_encoding(responseHeaders, SOUP_ENCODING_CHUNKED); |
| addContentDispositionHTTPHeaderToResponse(message); |
| g_signal_connect(message, "wrote_headers", G_CALLBACK(writeNextChunk), nullptr); |
| g_signal_connect(message, "wrote_chunk", G_CALLBACK(writeNextChunk), nullptr); |
| return; |
| } |
| |
| if (g_str_equal(path, "/ua-test-redirect")) { |
| soup_server_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY, nullptr); |
| soup_message_headers_append(responseHeaders, "Location", "/ua-test"); |
| return; |
| } |
| |
| if (g_str_equal(path, "/unknown") || g_str_equal(path, "/ua-test")) |
| path = "/test.pdf"; |
| |
| GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), path, nullptr)); |
| char* contents; |
| gsize contentsLength; |
| if (!g_file_get_contents(filePath.get(), &contents, &contentsLength, 0)) { |
| soup_server_message_set_status(message, SOUP_STATUS_NOT_FOUND, nullptr); |
| soup_message_body_complete(responseBody); |
| return; |
| } |
| |
| addContentDispositionHTTPHeaderToResponse(message); |
| soup_message_body_append(responseBody, SOUP_MEMORY_TAKE, contents, contentsLength); |
| soup_message_body_complete(responseBody); |
| } |
| |
| static void testDownloadRemoteFile(DownloadTest* test, gconstpointer) |
| { |
| GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf")); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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(); |
| |
| WebKitURIRequest* request = webkit_download_get_request(download.get()); |
| g_assert_nonnull(request); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/test.pdf")); |
| |
| auto headers = webkit_uri_request_get_http_headers(request); |
| g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent")); |
| |
| g_assert_nonnull(webkit_download_get_destination(download.get())); |
| g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1); |
| GUniquePtr<char> expectedFilename(g_strdup_printf("%s.pdf", kServerSuggestedFilename)); |
| test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get()); |
| } |
| |
| static void testDownloadRemoteFileError(DownloadErrorTest* test, gconstpointer) |
| { |
| test->m_expectedError = DownloadErrorTest::NetworkError; |
| GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/foo")); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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 = DownloadErrorTest::InvalidDestination; |
| download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf")); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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 = DownloadErrorTest::DownloadCancelled; |
| download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/cancel-after-destination")); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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); |
| // Check the intermediate file is deleted when the download is cancelled. |
| GUniquePtr<char> intermediateURI(g_strdup_printf("%s.wkdownload", webkit_download_get_destination(download.get()))); |
| GRefPtr<GFile> intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.get())); |
| g_assert_false(g_file_query_exists(intermediateFile.get(), nullptr)); |
| } |
| |
| class WebViewDownloadTest: public WebViewTest { |
| public: |
| MAKE_GLIB_TEST_FIXTURE(WebViewDownloadTest); |
| |
| static void downloadStartedCallback(WebKitWebContext* context, WebKitDownload* download, WebViewDownloadTest* test) |
| { |
| test->m_download = download; |
| test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download)); |
| g_signal_connect(download, "decide-destination", G_CALLBACK(downloadDecideDestinationCallback), test); |
| g_signal_connect(download, "finished", G_CALLBACK(downloadFinishedCallback), test); |
| test->quitMainLoop(); |
| } |
| |
| WebViewDownloadTest() |
| { |
| g_signal_connect(webkit_web_view_get_context(m_webView), "download-started", G_CALLBACK(downloadStartedCallback), this); |
| } |
| |
| ~WebViewDownloadTest() |
| { |
| g_signal_handlers_disconnect_matched(webkit_web_view_get_context(m_webView), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); |
| } |
| |
| void waitUntilDownloadStarted() |
| { |
| m_download = 0; |
| g_main_loop_run(m_mainLoop); |
| g_assert_nonnull(m_download.get()); |
| } |
| |
| static gboolean downloadDecideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, WebViewDownloadTest* test) |
| { |
| GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr)); |
| GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0)); |
| if (test->m_shouldDelayDecideDestination) |
| g_usleep(0.2 * G_USEC_PER_SEC); |
| webkit_download_set_destination(download, destinationURI.get()); |
| return TRUE; |
| } |
| |
| static void downloadFinishedCallback(WebKitDownload* download, WebViewDownloadTest* test) |
| { |
| test->quitMainLoop(); |
| } |
| |
| void waitUntilDownloadFinished() |
| { |
| g_main_loop_run(m_mainLoop); |
| } |
| |
| #if PLATFORM(GTK) |
| static gboolean contextMenuCallback(WebKitWebView* webView, WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult* hitTestResult, WebViewDownloadTest* test) |
| { |
| g_assert_true(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult)); |
| GList* items = webkit_context_menu_get_items(contextMenu); |
| for (GList* l = items; l; l = g_list_next(l)) { |
| g_assert_true(WEBKIT_IS_CONTEXT_MENU_ITEM(l->data)); |
| auto* item = WEBKIT_CONTEXT_MENU_ITEM(l->data); |
| if (webkit_context_menu_item_get_stock_action(item) == WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK) { |
| test->m_contextMenuDownloadItem = item; |
| break; |
| } |
| } |
| test->quitMainLoop(); |
| return FALSE; |
| } |
| |
| WebKitContextMenuItem* showContextMenuAndGetDownloadItem(int x, int y) |
| { |
| m_contextMenuDownloadItem = nullptr; |
| auto id = g_signal_connect(m_webView, "context-menu", G_CALLBACK(contextMenuCallback), this); |
| RunLoop::main().dispatch([this, x, y] { |
| clickMouseButton(x, y, 3); |
| }); |
| g_main_loop_run(m_mainLoop); |
| g_signal_handler_disconnect(m_webView, id); |
| return m_contextMenuDownloadItem.get(); |
| } |
| #endif |
| |
| GRefPtr<WebKitDownload> m_download; |
| bool m_shouldDelayDecideDestination { false }; |
| #if PLATFORM(GTK) |
| GRefPtr<WebKitContextMenuItem> m_contextMenuDownloadItem; |
| #endif |
| }; |
| |
| static void testWebViewDownloadURI(WebViewDownloadTest* test, gconstpointer) |
| { |
| GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_view_download_uri(test->m_webView, kServer->getURIForPath("/test.pdf").data())); |
| test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get())); |
| test->waitUntilDownloadStarted(); |
| g_assert_true(test->m_webView == webkit_download_get_web_view(download.get())); |
| |
| WebKitURIRequest* request = webkit_download_get_request(download.get()); |
| g_assert_nonnull(request); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/test.pdf")); |
| |
| auto headers = webkit_uri_request_get_http_headers(request); |
| g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent")); |
| test->waitUntilDownloadFinished(); |
| |
| GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(download.get()))); |
| GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0)); |
| g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0); |
| g_file_delete(downloadFile.get(), 0, 0); |
| } |
| |
| class PolicyResponseDownloadTest: public WebViewDownloadTest { |
| public: |
| MAKE_GLIB_TEST_FIXTURE(PolicyResponseDownloadTest); |
| |
| static gboolean decidePolicyCallback(WebKitWebView* webView, WebKitPolicyDecision* decision, WebKitPolicyDecisionType type, PolicyResponseDownloadTest* test) |
| { |
| if (type != WEBKIT_POLICY_DECISION_TYPE_RESPONSE) |
| return FALSE; |
| |
| webkit_policy_decision_download(decision); |
| return TRUE; |
| } |
| |
| PolicyResponseDownloadTest() |
| { |
| g_signal_connect(m_webView, "decide-policy", G_CALLBACK(decidePolicyCallback), this); |
| } |
| |
| ~PolicyResponseDownloadTest() |
| { |
| g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this); |
| } |
| |
| void cancelDownloadAndWaitUntilFinished() |
| { |
| webkit_download_cancel(m_download.get()); |
| waitUntilDownloadFinished(); |
| m_download = 0; |
| } |
| }; |
| |
| static void testPolicyResponseDownload(PolicyResponseDownloadTest* test, gconstpointer) |
| { |
| CString requestURI = kServer->getURIForPath("/test.pdf").data(); |
| // Delay the DecideDestination to ensure that the load is aborted before the network task has became a download. |
| // See https://bugs.webkit.org/show_bug.cgi?id=164220. |
| test->m_shouldDelayDecideDestination = true; |
| test->loadURI(requestURI.data()); |
| test->waitUntilDownloadStarted(); |
| |
| WebKitURIRequest* request = webkit_download_get_request(test->m_download.get()); |
| g_assert_nonnull(request); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI); |
| |
| g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get())); |
| |
| auto headers = webkit_uri_request_get_http_headers(request); |
| g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent")); |
| test->waitUntilDownloadFinished(); |
| |
| GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get()))); |
| GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr)); |
| g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0); |
| g_file_delete(downloadFile.get(), nullptr, nullptr); |
| } |
| |
| static void testPolicyResponseDownloadCancel(PolicyResponseDownloadTest* test, gconstpointer) |
| { |
| CString requestURI = kServer->getURIForPath("/test.pdf").data(); |
| test->loadURI(requestURI.data()); |
| test->waitUntilDownloadStarted(); |
| |
| WebKitURIRequest* request = webkit_download_get_request(test->m_download.get()); |
| g_assert_nonnull(request); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI); |
| |
| g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get())); |
| |
| auto headers = webkit_uri_request_get_http_headers(request); |
| g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent")); |
| test->cancelDownloadAndWaitUntilFinished(); |
| } |
| |
| static void testDownloadMIMEType(DownloadTest* test, gconstpointer) |
| { |
| GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/unknown")); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| |
| 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(); |
| |
| WebKitURIRequest* request = webkit_download_get_request(download.get()); |
| g_assert_true(WEBKIT_IS_URI_REQUEST(request)); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/unknown")); |
| |
| auto headers = webkit_uri_request_get_http_headers(request); |
| g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent")); |
| |
| WebKitURIResponse* response = webkit_download_get_response(download.get()); |
| g_assert_true(WEBKIT_IS_URI_RESPONSE(response)); |
| g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "application/pdf"); |
| |
| g_assert_nonnull(webkit_download_get_destination(download.get())); |
| g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1); |
| GUniquePtr<char> expectedFilename(g_strdup_printf("%s.pdf", kServerSuggestedFilename)); |
| test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get()); |
| } |
| |
| static void testDownloadUserAgent(DownloadTest* test, gconstpointer) |
| { |
| s_userAgentMap.clear(); |
| GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/ua-test")); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| WebKitURIRequest* request = webkit_download_get_request(download.get()); |
| g_assert_true(WEBKIT_IS_URI_REQUEST(request)); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/ua-test")); |
| |
| const char* userAgent = soup_message_headers_get_one(webkit_uri_request_get_http_headers(request), "User-Agent"); |
| g_assert_nonnull(userAgent); |
| g_assert_cmpuint(s_userAgentMap.size(), ==, 1); |
| g_assert_true(s_userAgentMap.contains("/ua-test")); |
| ASSERT_CMP_CSTRING(userAgent, ==, s_userAgentMap.get("/ua-test")); |
| s_userAgentMap.clear(); |
| |
| GUniquePtr<char> expectedFilename(g_strdup_printf("%s.pdf", kServerSuggestedFilename)); |
| test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get()); |
| |
| download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/ua-test-redirect")); |
| g_assert_null(webkit_download_get_web_view(download.get())); |
| request = webkit_download_get_request(download.get()); |
| g_assert_true(WEBKIT_IS_URI_REQUEST(request)); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/ua-test-redirect")); |
| |
| userAgent = soup_message_headers_get_one(webkit_uri_request_get_http_headers(request), "User-Agent"); |
| g_assert_nonnull(userAgent); |
| g_assert_cmpuint(s_userAgentMap.size(), ==, 2); |
| g_assert_true(s_userAgentMap.contains("/ua-test-redirect")); |
| ASSERT_CMP_CSTRING(userAgent, ==, s_userAgentMap.get("/ua-test-redirect")); |
| g_assert_true(s_userAgentMap.contains("/ua-test")); |
| ASSERT_CMP_CSTRING(userAgent, ==, s_userAgentMap.get("/ua-test")); |
| s_userAgentMap.clear(); |
| |
| test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get()); |
| } |
| |
| static void testDownloadEphemeralContext(Test* test, gconstpointer) |
| { |
| GRefPtr<WebKitWebsiteDataManager> manager = adoptGRef(webkit_website_data_manager_new_ephemeral()); |
| test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(manager.get())); |
| GRefPtr<WebKitWebContext> context = adoptGRef(webkit_web_context_new_with_website_data_manager(manager.get())); |
| test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(context.get())); |
| g_assert_true(webkit_web_context_is_ephemeral(context.get())); |
| |
| GRefPtr<GMainLoop> mainLoop = adoptGRef(g_main_loop_new(nullptr, TRUE)); |
| GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(context.get(), kServer->getURIForPath("/test.pdf").data())); |
| test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get())); |
| g_signal_connect(download.get(), "decide-destination", G_CALLBACK(+[](WebKitDownload* download, const gchar* suggestedFilename, gpointer) { |
| GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr)); |
| GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), nullptr, nullptr)); |
| webkit_download_set_destination(download, destinationURI.get()); |
| }), nullptr); |
| g_signal_connect(download.get(), "finished", G_CALLBACK(+[](WebKitDownload*, GMainLoop* loop) { |
| g_main_loop_quit(loop); |
| }), mainLoop.get()); |
| |
| g_main_loop_run(mainLoop.get()); |
| |
| GRefPtr<GFile> destFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(download.get()))); |
| g_file_delete(destFile.get(), nullptr, nullptr); |
| } |
| |
| #if PLATFORM(GTK) |
| static void testContextMenuDownloadActions(WebViewDownloadTest* test, gconstpointer) |
| { |
| test->showInWindow(); |
| |
| static const char* linkHTMLFormat = "<html><body><a style='position:absolute; left:1; top:1' href='%s'>Download Me</a></body></html>"; |
| GUniquePtr<char> linkHTML(g_strdup_printf(linkHTMLFormat, kServer->getURIForPath("/test.pdf").data())); |
| test->loadHtml(linkHTML.get(), kServer->getURIForPath("/").data()); |
| test->waitUntilLoadFinished(); |
| |
| auto* item = test->showContextMenuAndGetDownloadItem(1, 1); |
| g_assert(WEBKIT_IS_CONTEXT_MENU_ITEM(item)); |
| g_action_activate(webkit_context_menu_item_get_gaction(item), nullptr); |
| test->waitUntilDownloadStarted(); |
| |
| g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get())); |
| |
| WebKitURIRequest* request = webkit_download_get_request(test->m_download.get()); |
| g_assert_true(WEBKIT_IS_URI_REQUEST(request)); |
| ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/test.pdf")); |
| |
| auto headers = webkit_uri_request_get_http_headers(request); |
| g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent")); |
| |
| test->waitUntilDownloadFinished(); |
| |
| GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get()))); |
| GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr)); |
| g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0); |
| g_file_delete(downloadFile.get(), nullptr, nullptr); |
| } |
| |
| static void testBlobDownload(WebViewDownloadTest* test, gconstpointer) |
| { |
| test->showInWindow(); |
| |
| static const char* linkBlobHTML = |
| "<html><body>" |
| "<a id='downloadLink' style='position:absolute; left:1; top:1' download='foo.pdf'>Download Me</a>" |
| "<script>" |
| " blob = new Blob(['Hello world'], {type: 'text/plain'});" |
| " document.getElementById('downloadLink').href = window.URL.createObjectURL(blob);" |
| "</script>" |
| "</body></html>"; |
| test->loadHtml(linkBlobHTML, kServer->getURIForPath("/").data()); |
| test->waitUntilLoadFinished(); |
| |
| g_idle_add([](gpointer userData) -> gboolean { |
| auto* test = static_cast<WebViewDownloadTest*>(userData); |
| test->clickMouseButton(1, 1, 1); |
| return FALSE; |
| }, test); |
| test->waitUntilDownloadStarted(); |
| |
| g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get())); |
| test->waitUntilDownloadFinished(); |
| |
| GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get()))); |
| GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr)); |
| GUniquePtr<char> downloadPath(g_file_get_path(downloadFile.get())); |
| GUniqueOutPtr<char> downloadContents; |
| gsize downloadContentsLength; |
| g_assert_true(g_file_get_contents(downloadPath.get(), &downloadContents.outPtr(), &downloadContentsLength, nullptr)); |
| g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), ==, downloadContentsLength); |
| g_assert_cmpstr(downloadContents.get(), ==, "Hello world"); |
| g_file_delete(downloadFile.get(), nullptr, nullptr); |
| } |
| #endif // PLATFORM(GTK) |
| |
| void beforeAll() |
| { |
| kServer = new WebKitTestServer(); |
| kServer->run(serverCallback); |
| |
| DownloadTest::add("Downloads", "local-file", testDownloadLocalFile); |
| DownloadTest::add("Downloads", "overwrite-destination-allowed", testDownloadOverwriteDestinationAllowed); |
| DownloadErrorTest::add("Downloads", "overwrite-destination-disallowed", testDownloadOverwriteDestinationDisallowed); |
| DownloadErrorTest::add("Downloads", "local-file-error", testDownloadLocalFileError); |
| DownloadTest::add("Downloads", "remote-file", testDownloadRemoteFile); |
| DownloadErrorTest::add("Downloads", "remote-file-error", testDownloadRemoteFileError); |
| WebViewDownloadTest::add("WebKitWebView", "download-uri", testWebViewDownloadURI); |
| PolicyResponseDownloadTest::add("Downloads", "policy-decision-download", testPolicyResponseDownload); |
| PolicyResponseDownloadTest::add("Downloads", "policy-decision-download-cancel", testPolicyResponseDownloadCancel); |
| DownloadTest::add("Downloads", "mime-type", testDownloadMIMEType); |
| DownloadTest::add("Downloads", "user-agent", testDownloadUserAgent); |
| Test::add("Downloads", "ephemeral-context", testDownloadEphemeralContext); |
| // FIXME: Implement keyStroke in WPE. |
| #if PLATFORM(GTK) |
| #if !USE(GTK4) |
| // FIXME: Rework context menu API in GTK4 to not expose GdkEvent. |
| WebViewDownloadTest::add("Downloads", "contex-menu-download-actions", testContextMenuDownloadActions); |
| #endif |
| // FIXME: Implement mouse click in WPE. |
| WebViewDownloadTest::add("Downloads", "blob-download", testBlobDownload); |
| #endif |
| } |
| |
| void afterAll() |
| { |
| delete kServer; |
| } |