1/*
2 * Copyright (C) 2012, 2014 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21
22#include "WebKitTestServer.h"
23#include "WebViewTest.h"
24#include <glib/gstdio.h>
25#include <libsoup/soup.h>
26#include <string.h>
27#include <wtf/Vector.h>
28#include <wtf/glib/GRefPtr.h>
29#include <wtf/glib/GUniquePtr.h>
30#include <wtf/text/CString.h>
31
32class DownloadTest: public Test {
33public:
34 MAKE_GLIB_TEST_FIXTURE(DownloadTest);
35
36 enum DownloadEvent {
37 Started,
38 ReceivedResponse,
39 CreatedDestination,
40 ReceivedData,
41 Failed,
42 Finished
43 };
44
45 static void receivedResponseCallback(WebKitDownload* download, GParamSpec*, DownloadTest* test)
46 {
47 g_assert_nonnull(webkit_download_get_response(download));
48 test->receivedResponse(download);
49 }
50
51 static void createdDestinationCallback(WebKitDownload* download, const gchar* destination, DownloadTest* test)
52 {
53 g_assert_nonnull(webkit_download_get_destination(download));
54 g_assert_cmpstr(webkit_download_get_destination(download), ==, destination);
55 GRefPtr<GFile> file = adoptGRef(g_file_new_for_uri(destination));
56 g_assert_true(g_file_query_exists(file.get(), nullptr));
57 test->createdDestination(download, destination);
58 }
59
60 static void receivedDataCallback(WebKitDownload* download, guint64 dataLength, DownloadTest* test)
61 {
62 test->receivedData(download, dataLength);
63 }
64
65 static void finishedCallback(WebKitDownload* download, DownloadTest* test)
66 {
67 test->finished(download);
68 }
69
70 static void failedCallback(WebKitDownload* download, GError* error, DownloadTest* test)
71 {
72 g_assert_nonnull(error);
73
74 const char* destinationURI = webkit_download_get_destination(download);
75 if (destinationURI) {
76 GUniquePtr<char> tempFileURI(g_strconcat(destinationURI, ".wkdownload", nullptr));
77 GRefPtr<GFile> tempFile = adoptGRef(g_file_new_for_uri(tempFileURI.get()));
78 g_assert_false(g_file_query_exists(tempFile.get(), nullptr));
79 }
80
81 test->failed(download, error);
82 }
83
84 static gboolean decideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, DownloadTest* test)
85 {
86 g_assert_nonnull(suggestedFilename);
87 test->decideDestination(download, suggestedFilename);
88 return TRUE;
89 }
90
91 static void downloadStartedCallback(WebKitWebContext* context, WebKitDownload* download, DownloadTest* test)
92 {
93 g_assert_nonnull(webkit_download_get_request(download));
94 test->started(download);
95 g_signal_connect(download, "notify::response", G_CALLBACK(receivedResponseCallback), test);
96 g_signal_connect(download, "created-destination", G_CALLBACK(createdDestinationCallback), test);
97 g_signal_connect(download, "received-data", G_CALLBACK(receivedDataCallback), test);
98 g_signal_connect(download, "finished", G_CALLBACK(finishedCallback), test);
99 g_signal_connect(download, "failed", G_CALLBACK(failedCallback), test);
100 g_signal_connect(download, "decide-destination", G_CALLBACK(decideDestinationCallback), test);
101 }
102
103 DownloadTest()
104 : m_mainLoop(g_main_loop_new(nullptr, TRUE))
105 , m_downloadSize(0)
106 , m_allowOverwrite(false)
107 {
108 g_signal_connect(m_webContext.get(), "download-started", G_CALLBACK(downloadStartedCallback), this);
109 }
110
111 ~DownloadTest()
112 {
113 g_signal_handlers_disconnect_matched(m_webContext.get(), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
114 g_main_loop_unref(m_mainLoop);
115 }
116
117 virtual void started(WebKitDownload* download)
118 {
119 m_downloadSize = 0;
120 m_downloadEvents.clear();
121 m_downloadEvents.append(Started);
122 }
123
124 virtual void receivedResponse(WebKitDownload* download)
125 {
126 m_downloadEvents.append(ReceivedResponse);
127 }
128
129 virtual void createdDestination(WebKitDownload* download, const char* destination)
130 {
131 m_downloadEvents.append(CreatedDestination);
132 }
133
134 virtual void receivedData(WebKitDownload* download, guint64 dataLength)
135 {
136 m_downloadSize += dataLength;
137 if (!m_downloadEvents.contains(ReceivedData))
138 m_downloadEvents.append(ReceivedData);
139 }
140
141 virtual void finished(WebKitDownload* download)
142 {
143 g_assert_cmpuint(m_downloadSize, ==, webkit_download_get_received_data_length(download));
144 m_downloadEvents.append(Finished);
145 g_main_loop_quit(m_mainLoop);
146 }
147
148 virtual void failed(WebKitDownload* download, GError* error)
149 {
150 m_downloadEvents.append(Failed);
151 }
152
153 virtual void decideDestination(WebKitDownload* download, const gchar* suggestedFilename)
154 {
155 GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr));
156 GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0));
157 webkit_download_set_destination(download, destinationURI.get());
158 }
159
160 GRefPtr<WebKitDownload> downloadURIAndWaitUntilFinishes(const CString& requestURI)
161 {
162 GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_context_download_uri(m_webContext.get(), requestURI.data()));
163 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
164
165 g_assert_false(webkit_download_get_allow_overwrite(download.get()));
166 webkit_download_set_allow_overwrite(download.get(), m_allowOverwrite);
167 g_assert_cmpint(webkit_download_get_allow_overwrite(download.get()), ==, m_allowOverwrite);
168
169 WebKitURIRequest* request = webkit_download_get_request(download.get());
170 g_assert_nonnull(request);
171 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI);
172
173 g_main_loop_run(m_mainLoop);
174
175 return download;
176 }
177
178 void checkDestinationAndDeleteFile(WebKitDownload* download, const char* expectedName)
179 {
180 if (!webkit_download_get_destination(download))
181 return;
182 GRefPtr<GFile> destFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(download)));
183 GUniquePtr<char> destBasename(g_file_get_basename(destFile.get()));
184 g_assert_cmpstr(destBasename.get(), ==, expectedName);
185
186 g_file_delete(destFile.get(), 0, 0);
187 }
188
189 GMainLoop* m_mainLoop;
190 Vector<DownloadEvent> m_downloadEvents;
191 guint64 m_downloadSize;
192 bool m_allowOverwrite;
193};
194
195static GRefPtr<WebKitDownload> downloadLocalFileSuccessfully(DownloadTest* test, const char* filename)
196{
197 GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr));
198 GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get()));
199 GRefPtr<GFileInfo> sourceInfo = adoptGRef(g_file_query_info(source.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
200 GUniquePtr<char> sourceURI(g_file_get_uri(source.get()));
201 GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(sourceURI.get());
202 g_assert_null(webkit_download_get_web_view(download.get()));
203
204 Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
205 g_assert_cmpint(events.size(), ==, 5);
206 g_assert_cmpint(events[0], ==, DownloadTest::Started);
207 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
208 g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination);
209 g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData);
210 g_assert_cmpint(events[4], ==, DownloadTest::Finished);
211
212 WebKitURIRequest* request = webkit_download_get_request(download.get());
213 g_assert_nonnull(request);
214 g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, sourceURI.get());
215
216 g_assert_cmpint(test->m_downloadSize, ==, g_file_info_get_size(sourceInfo.get()));
217 g_assert_nonnull(webkit_download_get_destination(download.get()));
218 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1);
219
220 return download;
221}
222
223static void testDownloadLocalFile(DownloadTest* test, gconstpointer)
224{
225 static const char* filename = "test.pdf";
226 GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename);
227 test->checkDestinationAndDeleteFile(download.get(), filename);
228}
229
230static void createFileAtDestination(const char* filename)
231{
232 GUniquePtr<char> path(g_build_filename(Test::dataDirectory(), filename, nullptr));
233 GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get()));
234 GUniqueOutPtr<GError> error;
235 g_file_create(file.get(), G_FILE_CREATE_NONE, nullptr, &error.outPtr());
236 g_assert_no_error(error.get());
237 g_assert_true(g_file_query_exists(file.get(), nullptr));
238}
239
240static void testDownloadOverwriteDestinationAllowed(DownloadTest* test, gconstpointer)
241{
242 static const char* filename = "test.pdf";
243 createFileAtDestination(filename);
244
245 test->m_allowOverwrite = true;
246 GRefPtr<WebKitDownload> download = downloadLocalFileSuccessfully(test, filename);
247 test->checkDestinationAndDeleteFile(download.get(), filename);
248}
249
250class DownloadErrorTest: public DownloadTest {
251public:
252 MAKE_GLIB_TEST_FIXTURE(DownloadErrorTest);
253
254 enum ExpectedError {
255 NetworkError,
256 DownloadCancelled,
257 InvalidDestination,
258 DestinationExists
259 };
260
261 DownloadErrorTest()
262 : m_expectedError(NetworkError)
263 {
264 }
265
266 void receivedResponse(WebKitDownload* download)
267 {
268 DownloadTest::receivedResponse(download);
269 }
270
271 void createdDestination(WebKitDownload* download, const char* destination)
272 {
273 if (m_expectedError == DownloadCancelled)
274 webkit_download_cancel(download);
275 else
276 g_assert_not_reached();
277 }
278
279 void failed(WebKitDownload* download, GError* error)
280 {
281 g_assert_error(error, WEBKIT_DOWNLOAD_ERROR, expectedErrorToWebKitDownloadError(m_expectedError));
282 DownloadTest::failed(download, error);
283 }
284
285 void decideDestination(WebKitDownload* download, const gchar* suggestedFilename)
286 {
287 if (m_expectedError != InvalidDestination) {
288 DownloadTest::decideDestination(download, suggestedFilename);
289 return;
290 }
291 webkit_download_set_destination(download, "file:///foo/bar");
292 }
293
294 static WebKitDownloadError expectedErrorToWebKitDownloadError(ExpectedError expected)
295 {
296 switch (expected) {
297 case NetworkError:
298 return WEBKIT_DOWNLOAD_ERROR_NETWORK;
299 case DownloadCancelled:
300 return WEBKIT_DOWNLOAD_ERROR_CANCELLED_BY_USER;
301 case InvalidDestination:
302 return WEBKIT_DOWNLOAD_ERROR_DESTINATION;
303 case DestinationExists:
304 return WEBKIT_DOWNLOAD_ERROR_DESTINATION;
305 default:
306 g_assert_not_reached();
307 }
308 }
309
310 ExpectedError m_expectedError;
311};
312
313static void testDownloadOverwriteDestinationDisallowed(DownloadErrorTest* test, gconstpointer)
314{
315 static const char* filename = "test.pdf";
316 createFileAtDestination(filename);
317
318 test->m_expectedError = DownloadErrorTest::DestinationExists;
319 GUniquePtr<char> sourcePath(g_build_filename(Test::getResourcesDir().data(), filename, nullptr));
320 GRefPtr<GFile> source = adoptGRef(g_file_new_for_path(sourcePath.get()));
321 GUniquePtr<char> sourceURI(g_file_get_uri(source.get()));
322 GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(sourceURI.get());
323 g_assert_null(webkit_download_get_web_view(download.get()));
324
325 Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
326 g_assert_cmpint(events.size(), ==, 4);
327 g_assert_cmpint(events[0], ==, DownloadTest::Started);
328 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
329 g_assert_cmpint(events[2], ==, DownloadTest::Failed);
330 g_assert_cmpint(events[3], ==, DownloadTest::Finished);
331 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 0);
332
333 test->checkDestinationAndDeleteFile(download.get(), filename);
334}
335
336static void testDownloadLocalFileError(DownloadErrorTest* test, gconstpointer)
337{
338 test->m_expectedError = DownloadErrorTest::NetworkError;
339 GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes("file:///foo/bar");
340 g_assert_null(webkit_download_get_web_view(download.get()));
341
342 Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
343 g_assert_cmpint(events.size(), ==, 3);
344 g_assert_cmpint(events[0], ==, DownloadTest::Started);
345 g_assert_cmpint(events[1], ==, DownloadTest::Failed);
346 g_assert_cmpint(events[2], ==, DownloadTest::Finished);
347 events.clear();
348 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
349
350 test->m_expectedError = DownloadErrorTest::InvalidDestination;
351 GUniquePtr<char> path(g_build_filename(Test::getResourcesDir().data(), "test.pdf", nullptr));
352 GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(path.get()));
353 GUniquePtr<char> uri(g_file_get_uri(file.get()));
354 download = test->downloadURIAndWaitUntilFinishes(uri.get());
355 g_assert_null(webkit_download_get_web_view(download.get()));
356
357 g_assert_cmpint(events.size(), ==, 4);
358 g_assert_cmpint(events[0], ==, DownloadTest::Started);
359 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
360 g_assert_cmpint(events[2], ==, DownloadTest::Failed);
361 g_assert_cmpint(events[3], ==, DownloadTest::Finished);
362 events.clear();
363 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
364 test->checkDestinationAndDeleteFile(download.get(), "bar");
365
366 test->m_expectedError = DownloadErrorTest::DownloadCancelled;
367 download = test->downloadURIAndWaitUntilFinishes(uri.get());
368 g_assert_null(webkit_download_get_web_view(download.get()));
369
370 g_assert_cmpint(events.size(), ==, 4);
371 g_assert_cmpint(events[0], ==, DownloadTest::Started);
372 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
373 g_assert_cmpint(events[2], ==, DownloadTest::Failed);
374 g_assert_cmpint(events[3], ==, DownloadTest::Finished);
375 events.clear();
376 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
377 test->checkDestinationAndDeleteFile(download.get(), "test.pdf");
378}
379
380static WebKitTestServer* kServer;
381static const char* kServerSuggestedFilename = "webkit-downloaded-file";
382
383static void addContentDispositionHTTPHeaderToResponse(SoupMessage* message)
384{
385 GUniquePtr<char> contentDisposition(g_strdup_printf("attachment; filename=%s", kServerSuggestedFilename));
386 soup_message_headers_append(message->response_headers, "Content-Disposition", contentDisposition.get());
387}
388
389static void writeNextChunk(SoupMessage* message)
390{
391 /* We need a big enough chunk for the sniffer to not block the load */
392 static const char* chunk = "Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
393 "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
394 "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
395 "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
396 "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
397 "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!"
398 "Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!Testing!";
399 soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, chunk, strlen(chunk));
400}
401
402static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
403{
404 if (message->method != SOUP_METHOD_GET) {
405 soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
406 return;
407 }
408
409 soup_message_set_status(message, SOUP_STATUS_OK);
410
411 if (g_str_equal(path, "/cancel-after-destination")) {
412 // Use an infinite message to make sure it's cancelled before it finishes.
413 soup_message_headers_set_encoding(message->response_headers, SOUP_ENCODING_CHUNKED);
414 addContentDispositionHTTPHeaderToResponse(message);
415 g_signal_connect(message, "wrote_headers", G_CALLBACK(writeNextChunk), nullptr);
416 g_signal_connect(message, "wrote_chunk", G_CALLBACK(writeNextChunk), nullptr);
417 return;
418 }
419
420 if (g_str_equal(path, "/unknown"))
421 path = "/test.pdf";
422
423 GUniquePtr<char> filePath(g_build_filename(Test::getResourcesDir().data(), path, nullptr));
424 char* contents;
425 gsize contentsLength;
426 if (!g_file_get_contents(filePath.get(), &contents, &contentsLength, 0)) {
427 soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
428 soup_message_body_complete(message->response_body);
429 return;
430 }
431
432 addContentDispositionHTTPHeaderToResponse(message);
433 soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, contents, contentsLength);
434
435 soup_message_body_complete(message->response_body);
436}
437
438static void testDownloadRemoteFile(DownloadTest* test, gconstpointer)
439{
440 GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf"));
441 g_assert_null(webkit_download_get_web_view(download.get()));
442
443 Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
444 g_assert_cmpint(events.size(), ==, 5);
445 g_assert_cmpint(events[0], ==, DownloadTest::Started);
446 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
447 g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination);
448 g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData);
449 g_assert_cmpint(events[4], ==, DownloadTest::Finished);
450 events.clear();
451
452 WebKitURIRequest* request = webkit_download_get_request(download.get());
453 g_assert_nonnull(request);
454 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/test.pdf"));
455
456 auto headers = webkit_uri_request_get_http_headers(request);
457 g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent"));
458
459 g_assert_nonnull(webkit_download_get_destination(download.get()));
460 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1);
461 GUniquePtr<char> expectedFilename(g_strdup_printf("%s.pdf", kServerSuggestedFilename));
462 test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get());
463}
464
465static void testDownloadRemoteFileError(DownloadErrorTest* test, gconstpointer)
466{
467 test->m_expectedError = DownloadErrorTest::NetworkError;
468 GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/foo"));
469 g_assert_null(webkit_download_get_web_view(download.get()));
470
471 Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
472 g_assert_cmpint(events.size(), ==, 4);
473 g_assert_cmpint(events[0], ==, DownloadTest::Started);
474 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
475 g_assert_cmpint(events[2], ==, DownloadTest::Failed);
476 g_assert_cmpint(events[3], ==, DownloadTest::Finished);
477 events.clear();
478 WebKitURIResponse* response = webkit_download_get_response(download.get());
479 g_assert_cmpuint(webkit_uri_response_get_status_code(response), ==, 404);
480 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
481
482 test->m_expectedError = DownloadErrorTest::InvalidDestination;
483 download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/test.pdf"));
484 g_assert_null(webkit_download_get_web_view(download.get()));
485
486 g_assert_cmpint(events.size(), ==, 4);
487 g_assert_cmpint(events[0], ==, DownloadTest::Started);
488 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
489 g_assert_cmpint(events[2], ==, DownloadTest::Failed);
490 g_assert_cmpint(events[3], ==, DownloadTest::Finished);
491 events.clear();
492 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
493 test->checkDestinationAndDeleteFile(download.get(), "bar");
494
495 test->m_expectedError = DownloadErrorTest::DownloadCancelled;
496 download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/cancel-after-destination"));
497 g_assert_null(webkit_download_get_web_view(download.get()));
498
499 g_assert_cmpint(events.size(), ==, 4);
500 g_assert_cmpint(events[0], ==, DownloadTest::Started);
501 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
502 g_assert_cmpint(events[2], ==, DownloadTest::Failed);
503 g_assert_cmpint(events[3], ==, DownloadTest::Finished);
504 events.clear();
505 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), <, 1);
506 // Check the intermediate file is deleted when the download is cancelled.
507 GUniquePtr<char> intermediateURI(g_strdup_printf("%s.wkdownload", webkit_download_get_destination(download.get())));
508 GRefPtr<GFile> intermediateFile = adoptGRef(g_file_new_for_uri(intermediateURI.get()));
509 g_assert_false(g_file_query_exists(intermediateFile.get(), nullptr));
510}
511
512class WebViewDownloadTest: public WebViewTest {
513public:
514 MAKE_GLIB_TEST_FIXTURE(WebViewDownloadTest);
515
516 static void downloadStartedCallback(WebKitWebContext* context, WebKitDownload* download, WebViewDownloadTest* test)
517 {
518 test->m_download = download;
519 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download));
520 g_signal_connect(download, "decide-destination", G_CALLBACK(downloadDecideDestinationCallback), test);
521 g_signal_connect(download, "finished", G_CALLBACK(downloadFinishedCallback), test);
522 test->quitMainLoop();
523 }
524
525 WebViewDownloadTest()
526 {
527 g_signal_connect(webkit_web_view_get_context(m_webView), "download-started", G_CALLBACK(downloadStartedCallback), this);
528 }
529
530 ~WebViewDownloadTest()
531 {
532 g_signal_handlers_disconnect_matched(webkit_web_view_get_context(m_webView), G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
533 }
534
535 void waitUntilDownloadStarted()
536 {
537 m_download = 0;
538 g_main_loop_run(m_mainLoop);
539 g_assert_nonnull(m_download.get());
540 }
541
542 static gboolean downloadDecideDestinationCallback(WebKitDownload* download, const gchar* suggestedFilename, WebViewDownloadTest* test)
543 {
544 GUniquePtr<char> destination(g_build_filename(Test::dataDirectory(), suggestedFilename, nullptr));
545 GUniquePtr<char> destinationURI(g_filename_to_uri(destination.get(), 0, 0));
546 if (test->m_shouldDelayDecideDestination)
547 g_usleep(0.2 * G_USEC_PER_SEC);
548 webkit_download_set_destination(download, destinationURI.get());
549 return TRUE;
550 }
551
552 static void downloadFinishedCallback(WebKitDownload* download, WebViewDownloadTest* test)
553 {
554 test->quitMainLoop();
555 }
556
557 void waitUntilDownloadFinished()
558 {
559 g_main_loop_run(m_mainLoop);
560 }
561
562 GRefPtr<WebKitDownload> m_download;
563 bool m_shouldDelayDecideDestination { false };
564};
565
566static void testWebViewDownloadURI(WebViewDownloadTest* test, gconstpointer)
567{
568 GRefPtr<WebKitDownload> download = adoptGRef(webkit_web_view_download_uri(test->m_webView, kServer->getURIForPath("/test.pdf").data()));
569 test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(download.get()));
570 test->waitUntilDownloadStarted();
571 g_assert_true(test->m_webView == webkit_download_get_web_view(download.get()));
572
573 WebKitURIRequest* request = webkit_download_get_request(download.get());
574 g_assert_nonnull(request);
575 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/test.pdf"));
576
577 auto headers = webkit_uri_request_get_http_headers(request);
578 g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent"));
579 test->waitUntilDownloadFinished();
580
581 GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(download.get())));
582 GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), 0, 0));
583 g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0);
584 g_file_delete(downloadFile.get(), 0, 0);
585}
586
587class PolicyResponseDownloadTest: public WebViewDownloadTest {
588public:
589 MAKE_GLIB_TEST_FIXTURE(PolicyResponseDownloadTest);
590
591 static gboolean decidePolicyCallback(WebKitWebView* webView, WebKitPolicyDecision* decision, WebKitPolicyDecisionType type, PolicyResponseDownloadTest* test)
592 {
593 if (type != WEBKIT_POLICY_DECISION_TYPE_RESPONSE)
594 return FALSE;
595
596 webkit_policy_decision_download(decision);
597 return TRUE;
598 }
599
600 PolicyResponseDownloadTest()
601 {
602 g_signal_connect(m_webView, "decide-policy", G_CALLBACK(decidePolicyCallback), this);
603 }
604
605 ~PolicyResponseDownloadTest()
606 {
607 g_signal_handlers_disconnect_matched(m_webView, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, this);
608 }
609
610 void cancelDownloadAndWaitUntilFinished()
611 {
612 webkit_download_cancel(m_download.get());
613 waitUntilDownloadFinished();
614 m_download = 0;
615 }
616};
617
618static void testPolicyResponseDownload(PolicyResponseDownloadTest* test, gconstpointer)
619{
620 CString requestURI = kServer->getURIForPath("/test.pdf").data();
621 // Delay the DecideDestination to ensure that the load is aborted before the network task has became a download.
622 // See https://bugs.webkit.org/show_bug.cgi?id=164220.
623 test->m_shouldDelayDecideDestination = true;
624 test->loadURI(requestURI.data());
625 test->waitUntilDownloadStarted();
626
627 WebKitURIRequest* request = webkit_download_get_request(test->m_download.get());
628 g_assert_nonnull(request);
629 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI);
630
631 g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get()));
632
633 auto headers = webkit_uri_request_get_http_headers(request);
634 g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent"));
635 test->waitUntilDownloadFinished();
636
637 GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get())));
638 GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr));
639 g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0);
640 g_file_delete(downloadFile.get(), nullptr, nullptr);
641}
642
643static void testPolicyResponseDownloadCancel(PolicyResponseDownloadTest* test, gconstpointer)
644{
645 CString requestURI = kServer->getURIForPath("/test.pdf").data();
646 test->loadURI(requestURI.data());
647 test->waitUntilDownloadStarted();
648
649 WebKitURIRequest* request = webkit_download_get_request(test->m_download.get());
650 g_assert_nonnull(request);
651 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, requestURI);
652
653 g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get()));
654
655 auto headers = webkit_uri_request_get_http_headers(request);
656 g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent"));
657 test->cancelDownloadAndWaitUntilFinished();
658}
659
660static void testDownloadMIMEType(DownloadTest* test, gconstpointer)
661{
662 GRefPtr<WebKitDownload> download = test->downloadURIAndWaitUntilFinishes(kServer->getURIForPath("/unknown"));
663 g_assert_null(webkit_download_get_web_view(download.get()));
664
665 Vector<DownloadTest::DownloadEvent>& events = test->m_downloadEvents;
666 g_assert_cmpint(events.size(), ==, 5);
667 g_assert_cmpint(events[0], ==, DownloadTest::Started);
668 g_assert_cmpint(events[1], ==, DownloadTest::ReceivedResponse);
669 g_assert_cmpint(events[2], ==, DownloadTest::CreatedDestination);
670 g_assert_cmpint(events[3], ==, DownloadTest::ReceivedData);
671 g_assert_cmpint(events[4], ==, DownloadTest::Finished);
672 events.clear();
673
674 WebKitURIRequest* request = webkit_download_get_request(download.get());
675 WEBKIT_IS_URI_REQUEST(request);
676 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/unknown"));
677
678 auto headers = webkit_uri_request_get_http_headers(request);
679 g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent"));
680
681 WebKitURIResponse* response = webkit_download_get_response(download.get());
682 WEBKIT_IS_URI_RESPONSE(response);
683 g_assert_cmpstr(webkit_uri_response_get_mime_type(response), ==, "application/pdf");
684
685 g_assert_nonnull(webkit_download_get_destination(download.get()));
686 g_assert_cmpfloat(webkit_download_get_estimated_progress(download.get()), ==, 1);
687 GUniquePtr<char> expectedFilename(g_strdup_printf("%s.pdf", kServerSuggestedFilename));
688 test->checkDestinationAndDeleteFile(download.get(), expectedFilename.get());
689}
690
691#if PLATFORM(GTK)
692static gboolean contextMenuCallback(WebKitWebView* webView, WebKitContextMenu* contextMenu, GdkEvent*, WebKitHitTestResult* hitTestResult, WebViewDownloadTest* test)
693{
694 g_assert_true(WEBKIT_IS_HIT_TEST_RESULT(hitTestResult));
695 g_assert_true(webkit_hit_test_result_context_is_link(hitTestResult));
696 GList* items = webkit_context_menu_get_items(contextMenu);
697 GRefPtr<WebKitContextMenuItem> contextMenuItem;
698 for (GList* l = items; l; l = g_list_next(l)) {
699 g_assert_true(WEBKIT_IS_CONTEXT_MENU_ITEM(l->data));
700 auto* item = WEBKIT_CONTEXT_MENU_ITEM(l->data);
701 if (webkit_context_menu_item_get_stock_action(item) == WEBKIT_CONTEXT_MENU_ACTION_DOWNLOAD_LINK_TO_DISK) {
702 contextMenuItem = item;
703 break;
704 }
705 }
706 g_assert_nonnull(contextMenuItem.get());
707 webkit_context_menu_remove_all(contextMenu);
708 webkit_context_menu_append(contextMenu, contextMenuItem.get());
709 test->quitMainLoop();
710 return FALSE;
711}
712
713static void testContextMenuDownloadActions(WebViewDownloadTest* test, gconstpointer)
714{
715 test->showInWindowAndWaitUntilMapped();
716
717 static const char* linkHTMLFormat = "<html><body><a style='position:absolute; left:1; top:1' href='%s'>Download Me</a></body></html>";
718 GUniquePtr<char> linkHTML(g_strdup_printf(linkHTMLFormat, kServer->getURIForPath("/test.pdf").data()));
719 test->loadHtml(linkHTML.get(), kServer->getURIForPath("/").data());
720 test->waitUntilLoadFinished();
721
722 g_signal_connect(test->m_webView, "context-menu", G_CALLBACK(contextMenuCallback), test);
723 g_idle_add([](gpointer userData) -> gboolean {
724 auto* test = static_cast<WebViewDownloadTest*>(userData);
725 test->clickMouseButton(1, 1, 3);
726 return FALSE;
727 }, test);
728 g_main_loop_run(test->m_mainLoop);
729
730 g_idle_add([](gpointer userData) -> gboolean {
731 auto* test = static_cast<WebViewDownloadTest*>(userData);
732 // Select and activate the context menu action.
733 test->keyStroke(GDK_KEY_Down);
734 test->keyStroke(GDK_KEY_Return);
735 return FALSE;
736 }, test);
737 test->waitUntilDownloadStarted();
738
739 g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get()));
740
741 WebKitURIRequest* request = webkit_download_get_request(test->m_download.get());
742 WEBKIT_IS_URI_REQUEST(request);
743 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/test.pdf"));
744
745 auto headers = webkit_uri_request_get_http_headers(request);
746 g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent"));
747
748 test->waitUntilDownloadFinished();
749
750 GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get())));
751 GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr));
752 g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), >, 0);
753 g_file_delete(downloadFile.get(), nullptr, nullptr);
754}
755
756static void testBlobDownload(WebViewDownloadTest* test, gconstpointer)
757{
758 test->showInWindowAndWaitUntilMapped();
759
760 static const char* linkBlobHTML =
761 "<html><body>"
762 "<a id='downloadLink' style='position:absolute; left:1; top:1' download='foo.pdf'>Download Me</a>"
763 "<script>"
764 " blob = new Blob(['Hello world'], {type: 'text/plain'});"
765 " document.getElementById('downloadLink').href = window.URL.createObjectURL(blob);"
766 "</script>"
767 "</body></html>";
768 test->loadHtml(linkBlobHTML, kServer->getURIForPath("/").data());
769 test->waitUntilLoadFinished();
770
771 g_idle_add([](gpointer userData) -> gboolean {
772 auto* test = static_cast<WebViewDownloadTest*>(userData);
773 test->clickMouseButton(1, 1, 1);
774 return FALSE;
775 }, test);
776 test->waitUntilDownloadStarted();
777
778 g_assert_true(test->m_webView == webkit_download_get_web_view(test->m_download.get()));
779 test->waitUntilDownloadFinished();
780
781 GRefPtr<GFile> downloadFile = adoptGRef(g_file_new_for_uri(webkit_download_get_destination(test->m_download.get())));
782 GRefPtr<GFileInfo> downloadFileInfo = adoptGRef(g_file_query_info(downloadFile.get(), G_FILE_ATTRIBUTE_STANDARD_SIZE, static_cast<GFileQueryInfoFlags>(0), nullptr, nullptr));
783 GUniquePtr<char> downloadPath(g_file_get_path(downloadFile.get()));
784 GUniqueOutPtr<char> downloadContents;
785 gsize downloadContentsLength;
786 g_assert_true(g_file_get_contents(downloadPath.get(), &downloadContents.outPtr(), &downloadContentsLength, nullptr));
787 g_assert_cmpint(g_file_info_get_size(downloadFileInfo.get()), ==, downloadContentsLength);
788 g_assert_cmpstr(downloadContents.get(), ==, "Hello world");
789 g_file_delete(downloadFile.get(), nullptr, nullptr);
790}
791#endif // PLATFORM(GTK)
792
793void beforeAll()
794{
795 kServer = new WebKitTestServer();
796 kServer->run(serverCallback);
797
798 DownloadTest::add("Downloads", "local-file", testDownloadLocalFile);
799 DownloadTest::add("Downloads", "overwrite-destination-allowed", testDownloadOverwriteDestinationAllowed);
800 DownloadErrorTest::add("Downloads", "overwrite-destination-disallowed", testDownloadOverwriteDestinationDisallowed);
801 DownloadErrorTest::add("Downloads", "local-file-error", testDownloadLocalFileError);
802 DownloadTest::add("Downloads", "remote-file", testDownloadRemoteFile);
803 DownloadErrorTest::add("Downloads", "remote-file-error", testDownloadRemoteFileError);
804 WebViewDownloadTest::add("WebKitWebView", "download-uri", testWebViewDownloadURI);
805 PolicyResponseDownloadTest::add("Downloads", "policy-decision-download", testPolicyResponseDownload);
806 PolicyResponseDownloadTest::add("Downloads", "policy-decision-download-cancel", testPolicyResponseDownloadCancel);
807 DownloadTest::add("Downloads", "mime-type", testDownloadMIMEType);
808 // FIXME: Implement keyStroke in WPE.
809#if PLATFORM(GTK)
810 WebViewDownloadTest::add("Downloads", "contex-menu-download-actions", testContextMenuDownloadActions);
811 // FIXME: Implement mouse click in WPE.
812 WebViewDownloadTest::add("Downloads", "blob-download", testBlobDownload);
813#endif
814}
815
816void afterAll()
817{
818 delete kServer;
819}
820