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 | |
32 | class DownloadTest: public Test { |
33 | public: |
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 | |
195 | static 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 | |
223 | static 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 | |
230 | static 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 | |
240 | static 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 | |
250 | class DownloadErrorTest: public DownloadTest { |
251 | public: |
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 | |
313 | static 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 | |
336 | static 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 | |
380 | static WebKitTestServer* kServer; |
381 | static const char* kServerSuggestedFilename = "webkit-downloaded-file" ; |
382 | |
383 | static void (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 | |
389 | static 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 | |
402 | static 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 | |
438 | static 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 = 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 | |
465 | static 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 | |
512 | class WebViewDownloadTest: public WebViewTest { |
513 | public: |
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 | |
566 | static 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 = 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 | |
587 | class PolicyResponseDownloadTest: public WebViewDownloadTest { |
588 | public: |
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 | |
618 | static 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 = 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 | |
643 | static 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 = webkit_uri_request_get_http_headers(request); |
656 | g_assert_nonnull(soup_message_headers_get_one(headers, "User-Agent" )); |
657 | test->cancelDownloadAndWaitUntilFinished(); |
658 | } |
659 | |
660 | static 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 = 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) |
692 | static gboolean (WebKitWebView* webView, WebKitContextMenu* , 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> ; |
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 | |
713 | static void (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 = 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 | |
756 | static 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 | |
793 | void 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 | |
816 | void afterAll() |
817 | { |
818 | delete kServer; |
819 | } |
820 | |