1/*
2 * Copyright (C) 2012 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 "LoadTrackingTest.h"
23#include "WebKitTestServer.h"
24#include <wtf/glib/GRefPtr.h>
25#include <wtf/text/CString.h>
26
27static WebKitTestServer* kServer;
28
29class PolicyClientTest: public LoadTrackingTest {
30public:
31 MAKE_GLIB_TEST_FIXTURE(PolicyClientTest);
32
33 enum PolicyDecisionResponse {
34 Use,
35 Ignore,
36 Download,
37 None
38 };
39
40 PolicyClientTest()
41 : LoadTrackingTest()
42 , m_policyDecisionResponse(None)
43 , m_policyDecisionTypeFilter(0)
44 , m_respondToPolicyDecisionAsynchronously(false)
45 , m_haltMainLoopAfterMakingDecision(false)
46 {
47 g_signal_connect(m_webView, "decide-policy", G_CALLBACK(decidePolicyCallback), this);
48 }
49
50 static gboolean quitMainLoopLater(GMainLoop* loop)
51 {
52 g_main_loop_quit(loop);
53 return FALSE;
54 }
55
56 static void respondToPolicyDecision(PolicyClientTest* test, WebKitPolicyDecision* decision)
57 {
58 switch (test->m_policyDecisionResponse) {
59 case Use:
60 webkit_policy_decision_use(decision);
61 break;
62 case Ignore:
63 webkit_policy_decision_ignore(decision);
64 break;
65 case Download:
66 webkit_policy_decision_download(decision);
67 break;
68 case None:
69 break;
70 }
71
72 if (test->m_haltMainLoopAfterMakingDecision)
73 g_idle_add(reinterpret_cast<GSourceFunc>(quitMainLoopLater), test->m_mainLoop);
74 }
75
76 static gboolean respondToPolicyDecisionLater(PolicyClientTest* test)
77 {
78 respondToPolicyDecision(test, test->m_previousPolicyDecision.get());
79 test->m_previousPolicyDecision = 0;
80 return FALSE;
81 }
82
83 static gboolean decidePolicyCallback(WebKitWebView* webView, WebKitPolicyDecision* decision, WebKitPolicyDecisionType type, PolicyClientTest* test)
84 {
85 if (test->m_policyDecisionTypeFilter != type)
86 return FALSE;
87
88 test->m_previousPolicyDecision = decision;
89 if (test->m_respondToPolicyDecisionAsynchronously) {
90 g_idle_add(reinterpret_cast<GSourceFunc>(respondToPolicyDecisionLater), test);
91 return TRUE;
92 }
93
94 respondToPolicyDecision(test, decision);
95
96 // We return FALSE here to ensure that the default policy decision
97 // handler doesn't override whatever we use here.
98 return FALSE;
99 }
100
101 PolicyDecisionResponse m_policyDecisionResponse;
102 int m_policyDecisionTypeFilter;
103 bool m_respondToPolicyDecisionAsynchronously;
104 bool m_haltMainLoopAfterMakingDecision;
105 GRefPtr<WebKitPolicyDecision> m_previousPolicyDecision;
106};
107
108static void testNavigationPolicy(PolicyClientTest* test, gconstpointer)
109{
110 test->m_policyDecisionTypeFilter = WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION;
111
112 test->m_policyDecisionResponse = PolicyClientTest::Use;
113 test->loadHtml("<html/>", "http://webkitgtk.org/");
114 test->waitUntilLoadFinished();
115 g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
116
117 // Ideally we'd like to have a more intensive test here, but it's still pretty tricky
118 // to trigger different types of navigations with the GTK+ WebKit2 API.
119 WebKitNavigationPolicyDecision* decision = WEBKIT_NAVIGATION_POLICY_DECISION(test->m_previousPolicyDecision.get());
120 WebKitNavigationAction* navigationAction = webkit_navigation_policy_decision_get_navigation_action(decision);
121 g_assert_cmpint(webkit_navigation_action_get_navigation_type(navigationAction), ==, WEBKIT_NAVIGATION_TYPE_OTHER);
122 g_assert_cmpint(webkit_navigation_action_get_mouse_button(navigationAction), ==, 0);
123 g_assert_cmpint(webkit_navigation_action_get_modifiers(navigationAction), ==, 0);
124 g_assert_false(webkit_navigation_action_is_redirect(navigationAction));
125 g_assert_null(webkit_navigation_policy_decision_get_frame_name(decision));
126 WebKitURIRequest* request = webkit_navigation_action_get_request(navigationAction);
127 g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, "http://webkitgtk.org/");
128
129 test->m_policyDecisionResponse = PolicyClientTest::Use;
130 test->m_respondToPolicyDecisionAsynchronously = true;
131 test->loadHtml("<html/>", "http://webkitgtk.org/");
132 test->waitUntilLoadFinished();
133 g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
134
135 test->m_policyDecisionResponse = PolicyClientTest::Use;
136 test->m_respondToPolicyDecisionAsynchronously = false;
137 test->loadURI(kServer->getURIForPath("/redirect").data());
138 test->waitUntilLoadFinished();
139 g_assert_cmpint(test->m_loadEvents.size(), ==, 4);
140
141 decision = WEBKIT_NAVIGATION_POLICY_DECISION(test->m_previousPolicyDecision.get());
142 navigationAction = webkit_navigation_policy_decision_get_navigation_action(decision);
143 g_assert_true(webkit_navigation_action_is_redirect(navigationAction));
144 g_assert_null(webkit_navigation_policy_decision_get_frame_name(decision));
145 request = webkit_navigation_action_get_request(navigationAction);
146 g_assert_cmpstr(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/").data());
147
148 // If we are waiting until load completion, it will never complete if we ignore the
149 // navigation. So we tell the main loop to quit sometime later.
150 test->m_policyDecisionResponse = PolicyClientTest::Ignore;
151 test->m_respondToPolicyDecisionAsynchronously = false;
152 test->m_haltMainLoopAfterMakingDecision = true;
153 test->loadHtml("<html/>", "http://webkitgtk.org/");
154 test->waitUntilLoadFinished();
155 g_assert_cmpint(test->m_loadEvents.size(), ==, 0);
156
157 test->m_policyDecisionResponse = PolicyClientTest::Ignore;
158 test->loadHtml("<html/>", "http://webkitgtk.org/");
159 test->waitUntilLoadFinished();
160 g_assert_cmpint(test->m_loadEvents.size(), ==, 0);
161}
162
163static void testResponsePolicy(PolicyClientTest* test, gconstpointer)
164{
165 test->m_policyDecisionTypeFilter = WEBKIT_POLICY_DECISION_TYPE_RESPONSE;
166
167 test->m_policyDecisionResponse = PolicyClientTest::Use;
168 test->loadURI(kServer->getURIForPath("/").data());
169 test->waitUntilLoadFinished();
170 g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
171 g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
172 g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
173 g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
174
175 WebKitResponsePolicyDecision* decision = WEBKIT_RESPONSE_POLICY_DECISION(test->m_previousPolicyDecision.get());
176 WebKitURIRequest* request = webkit_response_policy_decision_get_request(decision);
177 g_assert_true(WEBKIT_IS_URI_REQUEST(request));
178 ASSERT_CMP_CSTRING(webkit_uri_request_get_uri(request), ==, kServer->getURIForPath("/"));
179 WebKitURIResponse* response = webkit_response_policy_decision_get_response(decision);
180 g_assert_true(WEBKIT_IS_URI_RESPONSE(response));
181 ASSERT_CMP_CSTRING(webkit_uri_response_get_uri(response), ==, kServer->getURIForPath("/"));
182 g_assert_cmpint(webkit_web_view_can_show_mime_type(test->m_webView, webkit_uri_response_get_mime_type(response)), ==,
183 webkit_response_policy_decision_is_mime_type_supported(decision));
184
185 test->m_respondToPolicyDecisionAsynchronously = true;
186 test->loadURI(kServer->getURIForPath("/").data());
187 test->waitUntilLoadFinished();
188 g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
189 g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
190 g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::LoadCommitted);
191 g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
192
193 test->m_respondToPolicyDecisionAsynchronously = false;
194 test->m_policyDecisionResponse = PolicyClientTest::Ignore;
195 test->loadURI(kServer->getURIForPath("/").data());
196 test->waitUntilLoadFinished();
197
198 g_assert_cmpint(test->m_loadEvents.size(), ==, 3);
199 g_assert_cmpint(test->m_loadEvents[0], ==, LoadTrackingTest::ProvisionalLoadStarted);
200 g_assert_cmpint(test->m_loadEvents[1], ==, LoadTrackingTest::ProvisionalLoadFailed);
201 g_assert_cmpint(test->m_loadEvents[2], ==, LoadTrackingTest::LoadFinished);
202}
203
204struct CreateCallbackData {
205 bool triedToOpenWindow;
206 GMainLoop* mainLoop;
207};
208
209static WebKitWebView* createCallback(WebKitWebView* webView, WebKitNavigationAction*, CreateCallbackData* data)
210{
211 data->triedToOpenWindow = true;
212 g_main_loop_quit(data->mainLoop);
213 return 0;
214}
215
216static void testNewWindowPolicy(PolicyClientTest* test, gconstpointer)
217{
218 static const char* windowOpeningHTML =
219 "<html><body>"
220 " <a id=\"link\" href=\"http://www.google.com\" target=\"_blank\">Link</a>"
221 " <script>"
222 " var event = document.createEvent('MouseEvents');"
223 " event.initEvent('click', true, false);"
224 " document.getElementById('link').dispatchEvent(event);"
225 " </script>"
226 "</body></html>";
227 test->m_policyDecisionTypeFilter = WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION;
228 webkit_settings_set_javascript_can_open_windows_automatically(webkit_web_view_get_settings(test->m_webView), TRUE);
229
230 CreateCallbackData data;
231 data.triedToOpenWindow = false;
232 data.mainLoop = test->m_mainLoop;
233
234 g_signal_connect(test->m_webView, "create", G_CALLBACK(createCallback), &data);
235 test->m_policyDecisionResponse = PolicyClientTest::Use;
236 test->loadHtml(windowOpeningHTML, "http://webkitgtk.org/");
237 test->wait(1);
238 g_assert_true(data.triedToOpenWindow);
239
240 WebKitNavigationPolicyDecision* decision = WEBKIT_NAVIGATION_POLICY_DECISION(test->m_previousPolicyDecision.get());
241 g_assert_cmpstr(webkit_navigation_policy_decision_get_frame_name(decision), ==, "_blank");
242
243 // Using a short timeout is a bit ugly here, but it's hard to get around because if we block
244 // the new window signal we cannot halt the main loop in the create callback. If we
245 // halt the main loop in the policy decision, the create callback never executes.
246 data.triedToOpenWindow = false;
247 test->m_policyDecisionResponse = PolicyClientTest::Ignore;
248 test->loadHtml(windowOpeningHTML, "http://webkitgtk.org/");
249 test->wait(.2);
250 g_assert_false(data.triedToOpenWindow);
251}
252
253static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
254{
255 if (message->method != SOUP_METHOD_GET) {
256 soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
257 return;
258 }
259
260 if (g_str_equal(path, "/")) {
261 static const char* responseString = "<html><body>Testing!</body></html>";
262 soup_message_set_status(message, SOUP_STATUS_OK);
263 soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, responseString, strlen(responseString));
264 soup_message_body_complete(message->response_body);
265 } else if (g_str_equal(path, "/redirect")) {
266 soup_message_set_status(message, SOUP_STATUS_MOVED_PERMANENTLY);
267 soup_message_headers_append(message->response_headers, "Location", "/");
268 } else
269 soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
270}
271
272void beforeAll()
273{
274 kServer = new WebKitTestServer();
275 kServer->run(serverCallback);
276
277 PolicyClientTest::add("WebKitPolicyClient", "navigation-policy", testNavigationPolicy);
278 PolicyClientTest::add("WebKitPolicyClient", "response-policy", testResponsePolicy);
279 PolicyClientTest::add("WebKitPolicyClient", "new-window-policy", testNewWindowPolicy);
280}
281
282void afterAll()
283{
284 delete kServer;
285}
286