]>
Commit | Line | Data |
---|---|---|
61b98a2d | 1 | ///////////////////////////////////////////////////////////////////////////// |
8290e3cd | 2 | // Name: src/gtk/webview_webkit.cpp |
61b98a2d SL |
3 | // Purpose: GTK WebKit backend for web view component |
4 | // Author: Marianne Gagnon, Robert Roebling | |
5 | // Id: $Id$ | |
6 | // Copyright: (c) 2010 Marianne Gagnon, 1998 Robert Roebling | |
7 | // Licence: wxWindows licence | |
8 | ///////////////////////////////////////////////////////////////////////////// | |
9 | ||
10 | // For compilers that support precompilation, includes "wx.h". | |
11 | #include "wx/wxprec.h" | |
12 | ||
13 | ||
14 | #if wxHAVE_WEB_BACKEND_GTK_WEBKIT | |
15 | ||
16 | #include "wx/stockitem.h" | |
8290e3cd | 17 | #include "wx/gtk/webview_webkit.h" |
61b98a2d SL |
18 | #include "wx/gtk/control.h" |
19 | #include "wx/gtk/private.h" | |
20 | #include "webkit/webkit.h" | |
21 | ||
22 | // ---------------------------------------------------------------------------- | |
23 | // GTK callbacks | |
24 | // ---------------------------------------------------------------------------- | |
25 | ||
26 | extern "C" | |
27 | { | |
28 | ||
29 | static void | |
30 | wxgtk_webkitctrl_load_status_callback(GtkWidget* widget, GParamSpec* arg1, | |
31 | wxWebViewGTKWebKit *webKitCtrl) | |
32 | { | |
33 | if (!webKitCtrl->m_ready) return; | |
34 | ||
35 | wxString url = webKitCtrl->GetCurrentURL(); | |
36 | ||
37 | WebKitLoadStatus status; | |
38 | g_object_get(G_OBJECT(widget), "load-status", &status, NULL); | |
39 | ||
40 | wxString target; // TODO: get target (if possible) | |
41 | ||
42 | if (status == WEBKIT_LOAD_FINISHED) | |
43 | { | |
44 | webKitCtrl->m_busy = false; | |
45 | wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_LOADED, | |
46 | webKitCtrl->GetId(), | |
47 | url, target, false); | |
48 | ||
49 | if (webKitCtrl && webKitCtrl->GetEventHandler()) | |
50 | webKitCtrl->GetEventHandler()->ProcessEvent(thisEvent); | |
51 | } | |
52 | else if (status == WEBKIT_LOAD_COMMITTED) | |
53 | { | |
54 | webKitCtrl->m_busy = true; | |
55 | wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_NAVIGATED, | |
56 | webKitCtrl->GetId(), | |
57 | url, target, false); | |
58 | ||
59 | if (webKitCtrl && webKitCtrl->GetEventHandler()) | |
60 | webKitCtrl->GetEventHandler()->ProcessEvent(thisEvent); | |
61 | } | |
62 | } | |
63 | ||
64 | static WebKitNavigationResponse | |
65 | wxgtk_webkitctrl_navigation_requ_callback(WebKitWebView *web_view, | |
66 | WebKitWebFrame *frame, | |
67 | WebKitNetworkRequest *request, | |
68 | wxWebViewGTKWebKit *webKitCtrl) | |
69 | { | |
70 | webKitCtrl->m_busy = true; | |
71 | ||
72 | const gchar* uri = webkit_network_request_get_uri(request); | |
73 | ||
74 | wxString target = webkit_web_frame_get_name (frame); | |
75 | wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_NAVIGATING, | |
76 | webKitCtrl->GetId(), | |
77 | wxString( uri, wxConvUTF8 ), | |
78 | target, | |
79 | true); | |
80 | ||
81 | if (webKitCtrl && webKitCtrl->GetEventHandler()) | |
82 | webKitCtrl->GetEventHandler()->ProcessEvent(thisEvent); | |
83 | ||
84 | if (thisEvent.IsVetoed()) | |
85 | { | |
86 | webKitCtrl->m_busy = false; | |
87 | return WEBKIT_NAVIGATION_RESPONSE_IGNORE; | |
88 | } | |
89 | else | |
90 | { | |
91 | return WEBKIT_NAVIGATION_RESPONSE_ACCEPT; | |
92 | } | |
93 | } | |
94 | ||
95 | static gboolean | |
96 | wxgtk_webkitctrl_error (WebKitWebView *web_view, | |
97 | WebKitWebFrame *web_frame, | |
98 | gchar *uri, | |
99 | gpointer web_error, | |
100 | wxWebViewGTKWebKit* webKitWindow) | |
101 | { | |
102 | webKitWindow->m_busy = false; | |
103 | wxWebNavigationError type = wxWEB_NAV_ERR_OTHER; | |
104 | ||
105 | GError* error = (GError*)web_error; | |
106 | wxString description(error->message, wxConvUTF8); | |
107 | ||
108 | if (strcmp(g_quark_to_string(error->domain), "soup_http_error_quark") == 0) | |
109 | { | |
110 | switch (error->code) | |
111 | { | |
112 | case SOUP_STATUS_CANCELLED: | |
113 | type = wxWEB_NAV_ERR_USER_CANCELLED; | |
114 | break; | |
115 | ||
116 | case SOUP_STATUS_CANT_RESOLVE: | |
117 | case SOUP_STATUS_NOT_FOUND: | |
118 | type = wxWEB_NAV_ERR_NOT_FOUND; | |
119 | break; | |
120 | ||
121 | case SOUP_STATUS_CANT_RESOLVE_PROXY: | |
122 | case SOUP_STATUS_CANT_CONNECT: | |
123 | case SOUP_STATUS_CANT_CONNECT_PROXY: | |
124 | case SOUP_STATUS_SSL_FAILED: | |
125 | case SOUP_STATUS_IO_ERROR: | |
126 | type = wxWEB_NAV_ERR_CONNECTION; | |
127 | break; | |
128 | ||
129 | case SOUP_STATUS_MALFORMED: | |
130 | //case SOUP_STATUS_TOO_MANY_REDIRECTS: | |
131 | type = wxWEB_NAV_ERR_REQUEST; | |
132 | break; | |
133 | ||
134 | //case SOUP_STATUS_NO_CONTENT: | |
135 | //case SOUP_STATUS_RESET_CONTENT: | |
136 | ||
137 | case SOUP_STATUS_BAD_REQUEST: | |
138 | type = wxWEB_NAV_ERR_REQUEST; | |
139 | break; | |
140 | ||
141 | case SOUP_STATUS_UNAUTHORIZED: | |
142 | case SOUP_STATUS_FORBIDDEN: | |
143 | type = wxWEB_NAV_ERR_AUTH; | |
144 | break; | |
145 | ||
146 | case SOUP_STATUS_METHOD_NOT_ALLOWED: | |
147 | case SOUP_STATUS_NOT_ACCEPTABLE: | |
148 | type = wxWEB_NAV_ERR_SECURITY; | |
149 | break; | |
150 | ||
151 | case SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED: | |
152 | type = wxWEB_NAV_ERR_AUTH; | |
153 | break; | |
154 | ||
155 | case SOUP_STATUS_REQUEST_TIMEOUT: | |
156 | type = wxWEB_NAV_ERR_CONNECTION; | |
157 | break; | |
158 | ||
159 | //case SOUP_STATUS_PAYMENT_REQUIRED: | |
160 | case SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE: | |
161 | case SOUP_STATUS_REQUEST_URI_TOO_LONG: | |
162 | case SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE: | |
163 | type = wxWEB_NAV_ERR_REQUEST; | |
164 | break; | |
165 | ||
166 | case SOUP_STATUS_BAD_GATEWAY: | |
167 | case SOUP_STATUS_SERVICE_UNAVAILABLE: | |
168 | case SOUP_STATUS_GATEWAY_TIMEOUT: | |
169 | type = wxWEB_NAV_ERR_CONNECTION; | |
170 | break; | |
171 | ||
172 | case SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED: | |
173 | type = wxWEB_NAV_ERR_REQUEST; | |
174 | break; | |
175 | //case SOUP_STATUS_INSUFFICIENT_STORAGE: | |
176 | //case SOUP_STATUS_NOT_EXTENDED: | |
177 | } | |
178 | } | |
179 | else if (strcmp(g_quark_to_string(error->domain), | |
180 | "webkit-network-error-quark") == 0) | |
181 | { | |
182 | switch (error->code) | |
183 | { | |
184 | //WEBKIT_NETWORK_ERROR_FAILED: | |
185 | //WEBKIT_NETWORK_ERROR_TRANSPORT: | |
186 | ||
187 | case WEBKIT_NETWORK_ERROR_UNKNOWN_PROTOCOL: | |
188 | type = wxWEB_NAV_ERR_REQUEST; | |
189 | break; | |
190 | ||
191 | case WEBKIT_NETWORK_ERROR_CANCELLED: | |
192 | type = wxWEB_NAV_ERR_USER_CANCELLED; | |
193 | break; | |
194 | ||
195 | case WEBKIT_NETWORK_ERROR_FILE_DOES_NOT_EXIST: | |
196 | type = wxWEB_NAV_ERR_NOT_FOUND; | |
197 | break; | |
198 | } | |
199 | } | |
200 | else if (strcmp(g_quark_to_string(error->domain), | |
201 | "webkit-policy-error-quark") == 0) | |
202 | { | |
203 | switch (error->code) | |
204 | { | |
205 | //case WEBKIT_POLICY_ERROR_FAILED: | |
206 | //case WEBKIT_POLICY_ERROR_CANNOT_SHOW_MIME_TYPE: | |
207 | //case WEBKIT_POLICY_ERROR_CANNOT_SHOW_URL: | |
208 | //case WEBKIT_POLICY_ERROR_FRAME_LOAD_INTERRUPTED_BY_POLICY_CHANGE: | |
209 | case WEBKIT_POLICY_ERROR_CANNOT_USE_RESTRICTED_PORT: | |
210 | type = wxWEB_NAV_ERR_SECURITY; | |
211 | break; | |
212 | } | |
213 | } | |
214 | /* | |
215 | webkit_plugin_error_quark | |
216 | else | |
217 | { | |
218 | printf("Error domain %s\n", g_quark_to_string(error->domain)); | |
219 | } | |
220 | */ | |
221 | ||
222 | wxWebNavigationEvent thisEvent(wxEVT_COMMAND_WEB_VIEW_ERROR, | |
223 | webKitWindow->GetId(), | |
224 | uri, | |
225 | wxEmptyString, | |
226 | false); | |
227 | thisEvent.SetString(description); | |
228 | thisEvent.SetInt(type); | |
229 | ||
230 | if (webKitWindow && webKitWindow->GetEventHandler()) | |
231 | { | |
232 | webKitWindow->GetEventHandler()->ProcessEvent(thisEvent); | |
233 | } | |
234 | ||
235 | return FALSE; | |
236 | } | |
237 | ||
238 | ||
239 | } // extern "C" | |
240 | ||
241 | //----------------------------------------------------------------------------- | |
242 | // wxWebViewGTKWebKit | |
243 | //----------------------------------------------------------------------------- | |
244 | ||
245 | //IMPLEMENT_DYNAMIC_CLASS(wxWebViewGTKWebKit, wxControl) | |
246 | ||
247 | bool wxWebViewGTKWebKit::Create(wxWindow *parent, | |
248 | wxWindowID id, | |
249 | const wxString &url, | |
250 | const wxPoint& pos, | |
251 | const wxSize& size, | |
252 | long style, | |
253 | const wxString& name) | |
254 | { | |
255 | m_ready = false; | |
256 | m_busy = false; | |
257 | ||
258 | if (!PreCreation( parent, pos, size ) || | |
259 | !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name )) | |
260 | { | |
261 | wxFAIL_MSG( wxT("wxWebViewGTKWebKit creation failed") ); | |
262 | return false; | |
263 | } | |
264 | ||
265 | GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL); | |
266 | web_view = webkit_web_view_new (); | |
267 | g_object_ref(web_view); // TODO: check memory management | |
268 | ||
269 | m_widget = scrolled_window; | |
270 | g_object_ref(m_widget); // TODO: check memory management | |
271 | ||
272 | /* Place the WebKitWebView in the GtkScrolledWindow */ | |
273 | gtk_container_add (GTK_CONTAINER (scrolled_window), web_view); | |
274 | gtk_widget_show(m_widget); | |
275 | gtk_widget_show(web_view); | |
276 | ||
277 | g_signal_connect_after(web_view, "notify::load-status", | |
278 | G_CALLBACK(wxgtk_webkitctrl_load_status_callback), | |
279 | this); | |
280 | g_signal_connect_after(web_view, "navigation-requested", | |
281 | G_CALLBACK(wxgtk_webkitctrl_navigation_requ_callback), | |
282 | this); | |
283 | g_signal_connect_after(web_view, "load-error", | |
284 | G_CALLBACK(wxgtk_webkitctrl_error), | |
285 | this); | |
286 | ||
287 | // this signal can be added if we care about new frames open requests | |
288 | //g_signal_connect_after(web_view, "new-window-policy-decision-requested", | |
289 | // G_CALLBACK(...), this); | |
290 | ||
291 | m_parent->DoAddChild( this ); | |
292 | ||
293 | PostCreation(size); | |
294 | ||
295 | /* Open a webpage */ | |
296 | webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), url); | |
297 | ||
298 | m_ready = true; | |
299 | ||
300 | return true; | |
301 | } | |
302 | ||
303 | bool wxWebViewGTKWebKit::Enable( bool enable ) | |
304 | { | |
305 | if (!wxControl::Enable(enable)) | |
306 | return false; | |
307 | ||
308 | gtk_widget_set_sensitive(GTK_BIN(m_widget)->child, enable); | |
309 | ||
310 | //if (enable) | |
311 | // GTKFixSensitivity(); | |
312 | ||
313 | return true; | |
314 | } | |
315 | ||
316 | GdkWindow* | |
317 | wxWebViewGTKWebKit::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const | |
318 | { | |
319 | GdkWindow* window = gtk_widget_get_parent_window(m_widget); | |
320 | return window; | |
321 | } | |
322 | ||
323 | void wxWebViewGTKWebKit::ZoomIn() | |
324 | { | |
325 | webkit_web_view_zoom_in (WEBKIT_WEB_VIEW(web_view)); | |
326 | } | |
327 | ||
328 | void wxWebViewGTKWebKit::ZoomOut() | |
329 | { | |
330 | webkit_web_view_zoom_out (WEBKIT_WEB_VIEW(web_view)); | |
331 | } | |
332 | ||
333 | void wxWebViewGTKWebKit::SetWebkitZoom(float level) | |
334 | { | |
335 | webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW(web_view), level); | |
336 | } | |
337 | ||
338 | float wxWebViewGTKWebKit::GetWebkitZoom() | |
339 | { | |
340 | return webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW(web_view)); | |
341 | } | |
342 | ||
343 | void wxWebViewGTKWebKit::Stop() | |
344 | { | |
345 | webkit_web_view_stop_loading (WEBKIT_WEB_VIEW(web_view)); | |
346 | } | |
347 | ||
348 | void wxWebViewGTKWebKit::Reload(wxWebViewReloadFlags flags) | |
349 | { | |
350 | if (flags & wxWEB_VIEW_RELOAD_NO_CACHE) | |
351 | { | |
352 | webkit_web_view_reload_bypass_cache (WEBKIT_WEB_VIEW(web_view)); | |
353 | } | |
354 | else | |
355 | { | |
356 | webkit_web_view_reload (WEBKIT_WEB_VIEW(web_view)); | |
357 | } | |
358 | } | |
359 | ||
360 | void wxWebViewGTKWebKit::LoadUrl(const wxString& url) | |
361 | { | |
362 | webkit_web_view_open(WEBKIT_WEB_VIEW(web_view), wxGTK_CONV(loc)); | |
363 | } | |
364 | ||
365 | ||
366 | void wxWebViewGTKWebKit::GoBack() | |
367 | { | |
368 | webkit_web_view_go_back (WEBKIT_WEB_VIEW(web_view)); | |
369 | } | |
370 | ||
371 | void wxWebViewGTKWebKit::GoForward() | |
372 | { | |
373 | webkit_web_view_go_forward (WEBKIT_WEB_VIEW(web_view)); | |
374 | } | |
375 | ||
376 | ||
377 | bool wxWebViewGTKWebKit::CanGoBack() | |
378 | { | |
379 | return webkit_web_view_can_go_back (WEBKIT_WEB_VIEW(web_view)); | |
380 | } | |
381 | ||
382 | ||
383 | bool wxWebViewGTKWebKit::CanGoForward() | |
384 | { | |
385 | return webkit_web_view_can_go_forward (WEBKIT_WEB_VIEW(web_view)); | |
386 | } | |
387 | ||
388 | ||
389 | wxString wxWebViewGTKWebKit::GetCurrentURL() | |
390 | { | |
391 | // FIXME: check which encoding the web kit control uses instead of | |
392 | // assuming UTF8 (here and elsewhere too) | |
393 | return wxString::FromUTF8(webkit_web_view_get_uri( | |
394 | WEBKIT_WEB_VIEW(web_view))); | |
395 | } | |
396 | ||
397 | ||
398 | wxString wxWebViewGTKWebKit::GetCurrentTitle() | |
399 | { | |
400 | return wxString::FromUTF8(webkit_web_view_get_title( | |
401 | WEBKIT_WEB_VIEW(web_view))); | |
402 | } | |
403 | ||
404 | ||
405 | wxString wxWebViewGTKWebKit::GetPageSource() | |
406 | { | |
407 | WebKitWebFrame* frame = webkit_web_view_get_main_frame( | |
408 | WEBKIT_WEB_VIEW(web_view)); | |
409 | WebKitWebDataSource* src = webkit_web_frame_get_data_source (frame); | |
410 | ||
411 | // TODO: check encoding with | |
412 | // const gchar* | |
413 | // webkit_web_data_source_get_encoding(WebKitWebDataSource *data_source); | |
414 | return wxString(webkit_web_data_source_get_data (src)->str, wxConvUTF8); | |
415 | } | |
416 | ||
417 | ||
418 | wxWebViewZoom wxWebViewGTKWebKit::GetZoom() | |
419 | { | |
420 | float zoom = GetWebkitZoom(); | |
421 | ||
422 | // arbitrary way to map float zoom to our common zoom enum | |
423 | if (zoom <= 0.65) | |
424 | { | |
425 | return wxWEB_VIEW_ZOOM_TINY; | |
426 | } | |
427 | else if (zoom > 0.65 && zoom <= 0.90) | |
428 | { | |
429 | return wxWEB_VIEW_ZOOM_SMALL; | |
430 | } | |
431 | else if (zoom > 0.90 && zoom <= 1.15) | |
432 | { | |
433 | return wxWEB_VIEW_ZOOM_MEDIUM; | |
434 | } | |
435 | else if (zoom > 1.15 && zoom <= 1.45) | |
436 | { | |
437 | return wxWEB_VIEW_ZOOM_LARGE; | |
438 | } | |
439 | else if (zoom > 1.45) | |
440 | { | |
441 | return wxWEB_VIEW_ZOOM_LARGEST; | |
442 | } | |
443 | ||
444 | // to shut up compilers, this can never be reached logically | |
445 | wxASSERT(false); | |
446 | return wxWEB_VIEW_ZOOM_MEDIUM; | |
447 | } | |
448 | ||
449 | ||
450 | void wxWebViewGTKWebKit::SetZoom(wxWebViewZoom zoom) | |
451 | { | |
452 | // arbitrary way to map our common zoom enum to float zoom | |
453 | switch (zoom) | |
454 | { | |
455 | case wxWEB_VIEW_ZOOM_TINY: | |
456 | SetWebkitZoom(0.6f); | |
457 | break; | |
458 | ||
459 | case wxWEB_VIEW_ZOOM_SMALL: | |
460 | SetWebkitZoom(0.8f); | |
461 | break; | |
462 | ||
463 | case wxWEB_VIEW_ZOOM_MEDIUM: | |
464 | SetWebkitZoom(1.0f); | |
465 | break; | |
466 | ||
467 | case wxWEB_VIEW_ZOOM_LARGE: | |
468 | SetWebkitZoom(1.3); | |
469 | break; | |
470 | ||
471 | case wxWEB_VIEW_ZOOM_LARGEST: | |
472 | SetWebkitZoom(1.6); | |
473 | break; | |
474 | ||
475 | default: | |
476 | wxASSERT(false); | |
477 | } | |
478 | } | |
479 | ||
480 | void wxWebViewGTKWebKit::SetZoomType(wxWebViewZoomType type) | |
481 | { | |
482 | webkit_web_view_set_full_content_zoom(WEBKIT_WEB_VIEW(web_view), | |
483 | (type == wxWEB_VIEW_ZOOM_TYPE_LAYOUT ? | |
484 | TRUE : FALSE)); | |
485 | } | |
486 | ||
487 | wxWebViewZoomType wxWebViewGTKWebKit::GetZoomType() const | |
488 | { | |
489 | gboolean fczoom = webkit_web_view_get_full_content_zoom( | |
490 | WEBKIT_WEB_VIEW(web_view)); | |
491 | ||
492 | if (fczoom) return wxWEB_VIEW_ZOOM_TYPE_LAYOUT; | |
493 | else return wxWEB_VIEW_ZOOM_TYPE_TEXT; | |
494 | } | |
495 | ||
496 | bool wxWebViewGTKWebKit::CanSetZoomType(wxWebViewZoomType) const | |
497 | { | |
498 | // this port supports all zoom types | |
499 | return true; | |
500 | } | |
501 | ||
502 | void wxWebViewGTKWebKit::SetPage(const wxString& html, const wxString& baseUri) | |
503 | { | |
504 | webkit_web_view_load_string (WEBKIT_WEB_VIEW(web_view), | |
505 | html.mb_str(wxConvUTF8), | |
506 | "text/html", | |
507 | "UTF-8", | |
508 | baseUri.mb_str(wxConvUTF8)); | |
509 | } | |
510 | ||
511 | void wxWebViewGTKWebKit::Print() | |
512 | { | |
513 | WebKitWebFrame* frame = webkit_web_view_get_main_frame( | |
514 | WEBKIT_WEB_VIEW(web_view)); | |
515 | webkit_web_frame_print (frame); | |
516 | ||
517 | // GtkPrintOperationResult webkit_web_frame_print_full | |
518 | // (WebKitWebFrame *frame, | |
519 | // GtkPrintOperation *operation, | |
520 | // GtkPrintOperationAction action, | |
521 | // GError **error); | |
522 | ||
523 | } | |
524 | ||
525 | ||
526 | bool wxWebViewGTKWebKit::IsBusy() | |
527 | { | |
528 | return m_busy; | |
529 | ||
530 | // This code looks nice but returns true after a page was cancelled | |
531 | /* | |
532 | WebKitLoadStatus status = webkit_web_view_get_load_status | |
533 | (WEBKIT_WEB_VIEW(web_view)); | |
534 | ||
535 | ||
536 | #if WEBKIT_CHECK_VERSION(1,1,16) | |
537 | // WEBKIT_LOAD_FAILED is new in webkit 1.1.16 | |
538 | if (status == WEBKIT_LOAD_FAILED) | |
539 | { | |
540 | return false; | |
541 | } | |
542 | #endif | |
543 | if (status == WEBKIT_LOAD_FINISHED) | |
544 | { | |
545 | return false; | |
546 | } | |
547 | ||
548 | return true; | |
549 | */ | |
550 | } | |
551 | ||
552 | // static | |
553 | wxVisualAttributes | |
554 | wxWebViewGTKWebKit::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant)) | |
555 | { | |
556 | return GetDefaultAttributesFromGTKWidget(webkit_web_view_new); | |
557 | } | |
558 | ||
559 | ||
560 | #endif // wxHAVE_WEB_BACKEND_GTK_WEBKIT |