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