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 | |
27 | static WebKitTestServer* kServer; |
28 | |
29 | class PolicyClientTest: public LoadTrackingTest { |
30 | public: |
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 | |
108 | static 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 | |
163 | static 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 | |
204 | struct CreateCallbackData { |
205 | bool triedToOpenWindow; |
206 | GMainLoop* mainLoop; |
207 | }; |
208 | |
209 | static WebKitWebView* createCallback(WebKitWebView* webView, WebKitNavigationAction*, CreateCallbackData* data) |
210 | { |
211 | data->triedToOpenWindow = true; |
212 | g_main_loop_quit(data->mainLoop); |
213 | return 0; |
214 | } |
215 | |
216 | static 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 | |
253 | static 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 | |
272 | void 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 | |
282 | void afterAll() |
283 | { |
284 | delete kServer; |
285 | } |
286 | |