]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/webview_webkit.cpp
Add basic history api and implement it under 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 //Get the initial history limit so we can enable and disable it later
327 WebKitWebBackForwardList* history;
328 history = webkit_web_view_get_back_forward_list(WEBKIT_WEB_VIEW(web_view));
329 m_historyLimit = webkit_web_back_forward_list_get_limit(history);
330
331 m_ready = true;
332
333 return true;
334 }
335
336 bool wxWebViewWebKit::Enable( bool enable )
337 {
338 if (!wxControl::Enable(enable))
339 return false;
340
341 gtk_widget_set_sensitive(GTK_BIN(m_widget)->child, enable);
342
343 //if (enable)
344 // GTKFixSensitivity();
345
346 return true;
347 }
348
349 GdkWindow*
350 wxWebViewWebKit::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
351 {
352 GdkWindow* window = gtk_widget_get_parent_window(m_widget);
353 return window;
354 }
355
356 void wxWebViewWebKit::ZoomIn()
357 {
358 webkit_web_view_zoom_in (WEBKIT_WEB_VIEW(web_view));
359 }
360
361 void wxWebViewWebKit::ZoomOut()
362 {
363 webkit_web_view_zoom_out (WEBKIT_WEB_VIEW(web_view));
364 }
365
366 void wxWebViewWebKit::SetWebkitZoom(float level)
367 {
368 webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW(web_view), level);
369 }
370
371 float wxWebViewWebKit::GetWebkitZoom()
372 {
373 return webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW(web_view));
374 }
375
376 void wxWebViewWebKit::Stop()
377 {
378 webkit_web_view_stop_loading (WEBKIT_WEB_VIEW(web_view));
379 }
380
381 void wxWebViewWebKit::Reload(wxWebViewReloadFlags flags)
382 {
383 if (flags & wxWEB_VIEW_RELOAD_NO_CACHE)
384 {
385 webkit_web_view_reload_bypass_cache (WEBKIT_WEB_VIEW(web_view));
386 }
387 else
388 {
389 webkit_web_view_reload (WEBKIT_WEB_VIEW(web_view));
390 }
391 }
392
393 void wxWebViewWebKit::LoadUrl(const wxString& url)
394 {
395 webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), wxGTK_CONV(url));
396 }
397
398
399 void wxWebViewWebKit::GoBack()
400 {
401 webkit_web_view_go_back (WEBKIT_WEB_VIEW(web_view));
402 }
403
404 void wxWebViewWebKit::GoForward()
405 {
406 webkit_web_view_go_forward (WEBKIT_WEB_VIEW(web_view));
407 }
408
409
410 bool wxWebViewWebKit::CanGoBack()
411 {
412 return webkit_web_view_can_go_back (WEBKIT_WEB_VIEW(web_view));
413 }
414
415
416 bool wxWebViewWebKit::CanGoForward()
417 {
418 return webkit_web_view_can_go_forward (WEBKIT_WEB_VIEW(web_view));
419 }
420
421 void wxWebViewWebKit::ClearHistory()
422 {
423 WebKitWebBackForwardList* history;
424 history = webkit_web_view_get_back_forward_list(WEBKIT_WEB_VIEW(web_view));
425 webkit_web_back_forward_list_clear(history);
426 }
427
428 void wxWebViewWebKit::EnableHistory(bool enable)
429 {
430 WebKitWebBackForwardList* history;
431 history = webkit_web_view_get_back_forward_list(WEBKIT_WEB_VIEW(web_view));
432 if(enable)
433 {
434 webkit_web_back_forward_list_set_limit(history, m_historyLimit);
435 }
436 else
437 {
438 webkit_web_back_forward_list_set_limit(history, 0);
439 }
440 }
441
442 wxString wxWebViewWebKit::GetCurrentURL()
443 {
444 // FIXME: check which encoding the web kit control uses instead of
445 // assuming UTF8 (here and elsewhere too)
446 return wxString::FromUTF8(webkit_web_view_get_uri(
447 WEBKIT_WEB_VIEW(web_view)));
448 }
449
450
451 wxString wxWebViewWebKit::GetCurrentTitle()
452 {
453 return wxString::FromUTF8(webkit_web_view_get_title(
454 WEBKIT_WEB_VIEW(web_view)));
455 }
456
457
458 wxString wxWebViewWebKit::GetPageSource()
459 {
460 WebKitWebFrame* frame = webkit_web_view_get_main_frame(
461 WEBKIT_WEB_VIEW(web_view));
462 WebKitWebDataSource* src = webkit_web_frame_get_data_source (frame);
463
464 // TODO: check encoding with
465 // const gchar*
466 // webkit_web_data_source_get_encoding(WebKitWebDataSource *data_source);
467 return wxString(webkit_web_data_source_get_data (src)->str, wxConvUTF8);
468 }
469
470
471 wxWebViewZoom wxWebViewWebKit::GetZoom()
472 {
473 float zoom = GetWebkitZoom();
474
475 // arbitrary way to map float zoom to our common zoom enum
476 if (zoom <= 0.65)
477 {
478 return wxWEB_VIEW_ZOOM_TINY;
479 }
480 else if (zoom > 0.65 && zoom <= 0.90)
481 {
482 return wxWEB_VIEW_ZOOM_SMALL;
483 }
484 else if (zoom > 0.90 && zoom <= 1.15)
485 {
486 return wxWEB_VIEW_ZOOM_MEDIUM;
487 }
488 else if (zoom > 1.15 && zoom <= 1.45)
489 {
490 return wxWEB_VIEW_ZOOM_LARGE;
491 }
492 else if (zoom > 1.45)
493 {
494 return wxWEB_VIEW_ZOOM_LARGEST;
495 }
496
497 // to shut up compilers, this can never be reached logically
498 wxASSERT(false);
499 return wxWEB_VIEW_ZOOM_MEDIUM;
500 }
501
502
503 void wxWebViewWebKit::SetZoom(wxWebViewZoom zoom)
504 {
505 // arbitrary way to map our common zoom enum to float zoom
506 switch (zoom)
507 {
508 case wxWEB_VIEW_ZOOM_TINY:
509 SetWebkitZoom(0.6f);
510 break;
511
512 case wxWEB_VIEW_ZOOM_SMALL:
513 SetWebkitZoom(0.8f);
514 break;
515
516 case wxWEB_VIEW_ZOOM_MEDIUM:
517 SetWebkitZoom(1.0f);
518 break;
519
520 case wxWEB_VIEW_ZOOM_LARGE:
521 SetWebkitZoom(1.3);
522 break;
523
524 case wxWEB_VIEW_ZOOM_LARGEST:
525 SetWebkitZoom(1.6);
526 break;
527
528 default:
529 wxASSERT(false);
530 }
531 }
532
533 void wxWebViewWebKit::SetZoomType(wxWebViewZoomType type)
534 {
535 webkit_web_view_set_full_content_zoom(WEBKIT_WEB_VIEW(web_view),
536 (type == wxWEB_VIEW_ZOOM_TYPE_LAYOUT ?
537 TRUE : FALSE));
538 }
539
540 wxWebViewZoomType wxWebViewWebKit::GetZoomType() const
541 {
542 gboolean fczoom = webkit_web_view_get_full_content_zoom(
543 WEBKIT_WEB_VIEW(web_view));
544
545 if (fczoom) return wxWEB_VIEW_ZOOM_TYPE_LAYOUT;
546 else return wxWEB_VIEW_ZOOM_TYPE_TEXT;
547 }
548
549 bool wxWebViewWebKit::CanSetZoomType(wxWebViewZoomType) const
550 {
551 // this port supports all zoom types
552 return true;
553 }
554
555 void wxWebViewWebKit::SetPage(const wxString& html, const wxString& baseUri)
556 {
557 webkit_web_view_load_string (WEBKIT_WEB_VIEW(web_view),
558 html.mb_str(wxConvUTF8),
559 "text/html",
560 "UTF-8",
561 baseUri.mb_str(wxConvUTF8));
562 }
563
564 void wxWebViewWebKit::Print()
565 {
566 WebKitWebFrame* frame = webkit_web_view_get_main_frame(
567 WEBKIT_WEB_VIEW(web_view));
568 webkit_web_frame_print (frame);
569
570 // GtkPrintOperationResult webkit_web_frame_print_full
571 // (WebKitWebFrame *frame,
572 // GtkPrintOperation *operation,
573 // GtkPrintOperationAction action,
574 // GError **error);
575
576 }
577
578
579 bool wxWebViewWebKit::IsBusy()
580 {
581 return m_busy;
582
583 // This code looks nice but returns true after a page was cancelled
584 /*
585 WebKitLoadStatus status = webkit_web_view_get_load_status
586 (WEBKIT_WEB_VIEW(web_view));
587
588
589 #if WEBKIT_CHECK_VERSION(1,1,16)
590 // WEBKIT_LOAD_FAILED is new in webkit 1.1.16
591 if (status == WEBKIT_LOAD_FAILED)
592 {
593 return false;
594 }
595 #endif
596 if (status == WEBKIT_LOAD_FINISHED)
597 {
598 return false;
599 }
600
601 return true;
602 */
603 }
604
605 // static
606 wxVisualAttributes
607 wxWebViewWebKit::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
608 {
609 return GetDefaultAttributesFromGTKWidget(webkit_web_view_new);
610 }
611
612
613 #endif // wxHAVE_WEB_BACKEND_GTK_WEBKIT