| 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 | |