1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/webview_webkit.cpp
3 // Purpose:     GTK WebKit backend for web view component
4 // Author:      Marianne Gagnon, Robert Roebling
5 // Copyright:   (c) 2010 Marianne Gagnon, 1998 Robert Roebling
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #if wxUSE_WEBVIEW && wxUSE_WEBVIEW_WEBKIT
13 
14 #include "wx/stockitem.h"
15 #include "wx/gtk/webview_webkit.h"
16 #include "wx/gtk/control.h"
17 #include "wx/gtk/private.h"
18 #include "wx/filesys.h"
19 #include "wx/base64.h"
20 #include "wx/log.h"
21 #include <webkit/webkit.h>
22 
23 // ----------------------------------------------------------------------------
24 // GTK callbacks
25 // ----------------------------------------------------------------------------
26 
27 extern "C"
28 {
29 
30 static void
wxgtk_webview_webkit_load_status(GtkWidget * widget,GParamSpec *,wxWebViewWebKit * webKitCtrl)31 wxgtk_webview_webkit_load_status(GtkWidget* widget,
32                                  GParamSpec*,
33                                  wxWebViewWebKit *webKitCtrl)
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         WebKitWebBackForwardList* hist = webkit_web_view_get_back_forward_list(WEBKIT_WEB_VIEW(widget));
45         WebKitWebHistoryItem* item = webkit_web_back_forward_list_get_current_item(hist);
46         //We have to check if we are actually storing history
47         //If the item isn't added we add it ourselves, it isn't added otherwise
48         //with a custom scheme.
49         if(!item || (WEBKIT_IS_WEB_HISTORY_ITEM(item) &&
50                      webkit_web_history_item_get_uri(item) != url))
51         {
52             WebKitWebHistoryItem*
53                 newitem = webkit_web_history_item_new_with_data
54                           (
55                             url.utf8_str(),
56                             webKitCtrl->GetCurrentTitle().utf8_str()
57                           );
58             webkit_web_back_forward_list_add_item(hist, newitem);
59         }
60 
61         webKitCtrl->m_busy = false;
62         wxWebViewEvent event(wxEVT_WEBVIEW_LOADED,
63                              webKitCtrl->GetId(),
64                              url, target);
65 
66         webKitCtrl->HandleWindowEvent(event);
67     }
68     else if (status ==  WEBKIT_LOAD_COMMITTED)
69     {
70         webKitCtrl->m_busy = true;
71         wxWebViewEvent event(wxEVT_WEBVIEW_NAVIGATED,
72                              webKitCtrl->GetId(),
73                              url, target);
74 
75         webKitCtrl->HandleWindowEvent(event);
76     }
77 }
78 
79 static gboolean
wxgtk_webview_webkit_navigation(WebKitWebView *,WebKitWebFrame * frame,WebKitNetworkRequest * request,WebKitWebNavigationAction *,WebKitWebPolicyDecision * policy_decision,wxWebViewWebKit * webKitCtrl)80 wxgtk_webview_webkit_navigation(WebKitWebView *,
81                                 WebKitWebFrame *frame,
82                                 WebKitNetworkRequest *request,
83                                 WebKitWebNavigationAction *,
84                                 WebKitWebPolicyDecision *policy_decision,
85                                 wxWebViewWebKit *webKitCtrl)
86 {
87     const gchar* uri = webkit_network_request_get_uri(request);
88     wxString target = webkit_web_frame_get_name (frame);
89 
90     //If m_creating is true then we are the result of a new window
91     //and so we need to send the event and veto the load
92     if(webKitCtrl->m_creating)
93     {
94         webKitCtrl->m_creating = false;
95         wxWebViewEvent event(wxEVT_WEBVIEW_NEWWINDOW,
96                              webKitCtrl->GetId(),
97                              wxString(uri, wxConvUTF8),
98                              target);
99 
100         webKitCtrl->HandleWindowEvent(event);
101 
102         webkit_web_policy_decision_ignore(policy_decision);
103         return TRUE;
104     }
105 
106     if(webKitCtrl->m_guard)
107     {
108         webKitCtrl->m_guard = false;
109         //We set this to make sure that we don't try to load the page again from
110         //the resource request callback
111         webKitCtrl->m_vfsurl = webkit_network_request_get_uri(request);
112         webkit_web_policy_decision_use(policy_decision);
113         return FALSE;
114     }
115 
116     webKitCtrl->m_busy = true;
117 
118     wxWebViewEvent event(wxEVT_WEBVIEW_NAVIGATING,
119                          webKitCtrl->GetId(),
120                          wxString( uri, wxConvUTF8 ),
121                          target);
122 
123     webKitCtrl->HandleWindowEvent(event);
124 
125     if (!event.IsAllowed())
126     {
127         webKitCtrl->m_busy = false;
128         webkit_web_policy_decision_ignore(policy_decision);
129         return TRUE;
130     }
131     else
132     {
133         wxString wxuri = uri;
134         wxSharedPtr<wxWebViewHandler> handler;
135         wxVector<wxSharedPtr<wxWebViewHandler> > hanlders = webKitCtrl->GetHandlers();
136         //We are not vetoed so see if we match one of the additional handlers
137         for(wxVector<wxSharedPtr<wxWebViewHandler> >::iterator it = hanlders.begin();
138             it != hanlders.end(); ++it)
139         {
140             if(wxuri.substr(0, (*it)->GetName().length()) == (*it)->GetName())
141             {
142                 handler = (*it);
143             }
144         }
145         //If we found a handler we can then use it to load the file directly
146         //ourselves
147         if(handler)
148         {
149             webKitCtrl->m_guard = true;
150             wxFSFile* file = handler->GetFile(wxuri);
151             if(file)
152             {
153                 webKitCtrl->SetPage(*file->GetStream(), wxuri);
154             }
155             //We need to throw some sort of error here if file is NULL
156             webkit_web_policy_decision_ignore(policy_decision);
157             return TRUE;
158         }
159         return FALSE;
160     }
161 }
162 
163 static gboolean
wxgtk_webview_webkit_error(WebKitWebView *,WebKitWebFrame *,gchar * uri,gpointer web_error,wxWebViewWebKit * webKitWindow)164 wxgtk_webview_webkit_error(WebKitWebView*,
165                            WebKitWebFrame*,
166                            gchar *uri,
167                            gpointer web_error,
168                            wxWebViewWebKit* webKitWindow)
169 {
170     webKitWindow->m_busy = false;
171     wxWebViewNavigationError type = wxWEBVIEW_NAV_ERR_OTHER;
172 
173     GError* error = (GError*)web_error;
174     wxString description(error->message, wxConvUTF8);
175 
176     if (strcmp(g_quark_to_string(error->domain), "soup_http_error_quark") == 0)
177     {
178         switch (error->code)
179         {
180             case SOUP_STATUS_CANCELLED:
181                 type = wxWEBVIEW_NAV_ERR_USER_CANCELLED;
182                 break;
183 
184             case SOUP_STATUS_CANT_RESOLVE:
185             case SOUP_STATUS_NOT_FOUND:
186                 type = wxWEBVIEW_NAV_ERR_NOT_FOUND;
187                 break;
188 
189             case SOUP_STATUS_CANT_RESOLVE_PROXY:
190             case SOUP_STATUS_CANT_CONNECT:
191             case SOUP_STATUS_CANT_CONNECT_PROXY:
192             case SOUP_STATUS_SSL_FAILED:
193             case SOUP_STATUS_IO_ERROR:
194                 type = wxWEBVIEW_NAV_ERR_CONNECTION;
195                 break;
196 
197             case SOUP_STATUS_MALFORMED:
198             //case SOUP_STATUS_TOO_MANY_REDIRECTS:
199                 type = wxWEBVIEW_NAV_ERR_REQUEST;
200                 break;
201 
202             //case SOUP_STATUS_NO_CONTENT:
203             //case SOUP_STATUS_RESET_CONTENT:
204 
205             case SOUP_STATUS_BAD_REQUEST:
206                 type = wxWEBVIEW_NAV_ERR_REQUEST;
207                 break;
208 
209             case SOUP_STATUS_UNAUTHORIZED:
210             case SOUP_STATUS_FORBIDDEN:
211                 type = wxWEBVIEW_NAV_ERR_AUTH;
212                 break;
213 
214             case SOUP_STATUS_METHOD_NOT_ALLOWED:
215             case SOUP_STATUS_NOT_ACCEPTABLE:
216                 type = wxWEBVIEW_NAV_ERR_SECURITY;
217                 break;
218 
219             case SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED:
220                 type = wxWEBVIEW_NAV_ERR_AUTH;
221                 break;
222 
223             case SOUP_STATUS_REQUEST_TIMEOUT:
224                 type = wxWEBVIEW_NAV_ERR_CONNECTION;
225                 break;
226 
227             //case SOUP_STATUS_PAYMENT_REQUIRED:
228             case SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE:
229             case SOUP_STATUS_REQUEST_URI_TOO_LONG:
230             case SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE:
231                 type = wxWEBVIEW_NAV_ERR_REQUEST;
232                 break;
233 
234             case SOUP_STATUS_BAD_GATEWAY:
235             case SOUP_STATUS_SERVICE_UNAVAILABLE:
236             case SOUP_STATUS_GATEWAY_TIMEOUT:
237                 type = wxWEBVIEW_NAV_ERR_CONNECTION;
238                 break;
239 
240             case SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED:
241                 type = wxWEBVIEW_NAV_ERR_REQUEST;
242                 break;
243             //case SOUP_STATUS_INSUFFICIENT_STORAGE:
244             //case SOUP_STATUS_NOT_EXTENDED:
245         }
246     }
247     else if (strcmp(g_quark_to_string(error->domain),
248                     "webkit-network-error-quark") == 0)
249     {
250         switch (error->code)
251         {
252             //WEBKIT_NETWORK_ERROR_FAILED:
253             //WEBKIT_NETWORK_ERROR_TRANSPORT:
254 
255             case WEBKIT_NETWORK_ERROR_UNKNOWN_PROTOCOL:
256                 type = wxWEBVIEW_NAV_ERR_REQUEST;
257                 break;
258 
259             case WEBKIT_NETWORK_ERROR_CANCELLED:
260                 type = wxWEBVIEW_NAV_ERR_USER_CANCELLED;
261                 break;
262 
263             case WEBKIT_NETWORK_ERROR_FILE_DOES_NOT_EXIST:
264                 type = wxWEBVIEW_NAV_ERR_NOT_FOUND;
265                 break;
266         }
267     }
268     else if (strcmp(g_quark_to_string(error->domain),
269                     "webkit-policy-error-quark") == 0)
270     {
271         switch (error->code)
272         {
273             //case WEBKIT_POLICY_ERROR_FAILED:
274             //case WEBKIT_POLICY_ERROR_CANNOT_SHOW_MIME_TYPE:
275             //case WEBKIT_POLICY_ERROR_CANNOT_SHOW_URL:
276             //case WEBKIT_POLICY_ERROR_FRAME_LOAD_INTERRUPTED_BY_POLICY_CHANGE:
277             case WEBKIT_POLICY_ERROR_CANNOT_USE_RESTRICTED_PORT:
278                 type = wxWEBVIEW_NAV_ERR_SECURITY;
279                 break;
280         }
281     }
282     /*
283     webkit_plugin_error_quark
284     else
285     {
286         printf("Error domain %s\n", g_quark_to_string(error->domain));
287     }
288     */
289 
290     wxWebViewEvent event(wxEVT_WEBVIEW_ERROR,
291                          webKitWindow->GetId(),
292                          uri, "");
293     event.SetString(description);
294     event.SetInt(type);
295 
296     webKitWindow->HandleWindowEvent(event);
297 
298     return FALSE;
299 }
300 
301 static gboolean
wxgtk_webview_webkit_new_window(WebKitWebView *,WebKitWebFrame * frame,WebKitNetworkRequest * request,WebKitWebNavigationAction *,WebKitWebPolicyDecision * policy_decision,wxWebViewWebKit * webKitCtrl)302 wxgtk_webview_webkit_new_window(WebKitWebView*,
303                                 WebKitWebFrame *frame,
304                                 WebKitNetworkRequest *request,
305                                 WebKitWebNavigationAction*,
306                                 WebKitWebPolicyDecision *policy_decision,
307                                 wxWebViewWebKit *webKitCtrl)
308 {
309     const gchar* uri = webkit_network_request_get_uri(request);
310 
311     wxString target = webkit_web_frame_get_name (frame);
312     wxWebViewEvent event(wxEVT_WEBVIEW_NEWWINDOW,
313                                        webKitCtrl->GetId(),
314                                        wxString( uri, wxConvUTF8 ),
315                                        target);
316 
317     webKitCtrl->HandleWindowEvent(event);
318 
319     //We always want the user to handle this themselves
320     webkit_web_policy_decision_ignore(policy_decision);
321     return TRUE;
322 }
323 
324 static void
wxgtk_webview_webkit_title_changed(WebKitWebView *,WebKitWebFrame *,gchar * title,wxWebViewWebKit * webKitCtrl)325 wxgtk_webview_webkit_title_changed(WebKitWebView*,
326                                    WebKitWebFrame*,
327                                    gchar *title,
328                                    wxWebViewWebKit *webKitCtrl)
329 {
330     wxWebViewEvent event(wxEVT_WEBVIEW_TITLE_CHANGED,
331                          webKitCtrl->GetId(),
332                          webKitCtrl->GetCurrentURL(),
333                          "");
334     event.SetString(wxString(title, wxConvUTF8));
335 
336     webKitCtrl->HandleWindowEvent(event);
337 }
338 
339 static void
wxgtk_webview_webkit_resource_req(WebKitWebView *,WebKitWebFrame *,WebKitWebResource *,WebKitNetworkRequest * request,WebKitNetworkResponse *,wxWebViewWebKit * webKitCtrl)340 wxgtk_webview_webkit_resource_req(WebKitWebView *,
341                                   WebKitWebFrame *,
342                                   WebKitWebResource *,
343                                   WebKitNetworkRequest *request,
344                                   WebKitNetworkResponse *,
345                                   wxWebViewWebKit *webKitCtrl)
346 {
347     wxString uri = webkit_network_request_get_uri(request);
348 
349     wxSharedPtr<wxWebViewHandler> handler;
350     wxVector<wxSharedPtr<wxWebViewHandler> > hanlders = webKitCtrl->GetHandlers();
351 
352     //We are not vetoed so see if we match one of the additional handlers
353     for(wxVector<wxSharedPtr<wxWebViewHandler> >::iterator it = hanlders.begin();
354         it != hanlders.end(); ++it)
355     {
356         if(uri.substr(0, (*it)->GetName().length()) == (*it)->GetName())
357         {
358             handler = (*it);
359         }
360     }
361     //If we found a handler we can then use it to load the file directly
362     //ourselves
363     if(handler)
364     {
365         //If it is requsting the page itself then return as we have already
366         //loaded it from the archive
367         if(webKitCtrl->m_vfsurl == uri)
368             return;
369 
370         wxFSFile* file = handler->GetFile(uri);
371         if(file)
372         {
373             //We load the data into a data url to save it being written out again
374             size_t size = file->GetStream()->GetLength();
375             char *buffer = new char[size];
376             file->GetStream()->Read(buffer, size);
377             wxString data = wxBase64Encode(buffer, size);
378             delete[] buffer;
379             wxString mime = file->GetMimeType();
380             wxString path = "data:" + mime + ";base64," + data;
381             //Then we can redirect the call
382             webkit_network_request_set_uri(request, path.utf8_str());
383         }
384 
385     }
386 }
387 
388 #if WEBKIT_CHECK_VERSION(1, 10, 0)
389 
390 static gboolean
wxgtk_webview_webkit_context_menu(WebKitWebView *,GtkWidget *,WebKitHitTestResult *,gboolean,wxWebViewWebKit * webKitCtrl)391 wxgtk_webview_webkit_context_menu(WebKitWebView *,
392                                   GtkWidget *,
393                                   WebKitHitTestResult *,
394                                   gboolean,
395                                   wxWebViewWebKit *webKitCtrl)
396 {
397     return !webKitCtrl->IsContextMenuEnabled();
398 }
399 
400 #endif
401 
402 static WebKitWebView*
wxgtk_webview_webkit_create_webview(WebKitWebView * web_view,WebKitWebFrame *,wxWebViewWebKit * webKitCtrl)403 wxgtk_webview_webkit_create_webview(WebKitWebView *web_view,
404                                     WebKitWebFrame*,
405                                     wxWebViewWebKit *webKitCtrl)
406 {
407     //As we do not know the uri being loaded at this point allow the load to
408     //continue and catch it in navigation-policy-decision-requested
409     webKitCtrl->m_creating = true;
410     return web_view;
411 }
412 
413 } // extern "C"
414 
415 //-----------------------------------------------------------------------------
416 // wxWebViewWebKit
417 //-----------------------------------------------------------------------------
418 
419 wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewWebKit, wxWebView);
420 
wxWebViewWebKit()421 wxWebViewWebKit::wxWebViewWebKit()
422 {
423     m_web_view = NULL;
424 }
425 
Create(wxWindow * parent,wxWindowID id,const wxString & url,const wxPoint & pos,const wxSize & size,long style,const wxString & name)426 bool wxWebViewWebKit::Create(wxWindow *parent,
427                       wxWindowID id,
428                       const wxString &url,
429                       const wxPoint& pos,
430                       const wxSize& size,
431                       long style,
432                       const wxString& name)
433 {
434     m_web_view = NULL;
435     m_busy = false;
436     m_guard = false;
437     m_creating = false;
438     FindClear();
439 
440     // We currently unconditionally impose scrolling in both directions as it's
441     // necessary to show arbitrary pages.
442     style |= wxHSCROLL | wxVSCROLL;
443 
444     if (!PreCreation( parent, pos, size ) ||
445         !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
446     {
447         wxFAIL_MSG( wxT("wxWebViewWebKit creation failed") );
448         return false;
449     }
450 
451     m_web_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
452     GTKCreateScrolledWindowWith(GTK_WIDGET(m_web_view));
453     g_object_ref(m_widget);
454 
455     g_signal_connect_after(m_web_view, "navigation-policy-decision-requested",
456                            G_CALLBACK(wxgtk_webview_webkit_navigation),
457                            this);
458     g_signal_connect_after(m_web_view, "load-error",
459                            G_CALLBACK(wxgtk_webview_webkit_error),
460                            this);
461 
462     g_signal_connect_after(m_web_view, "new-window-policy-decision-requested",
463                            G_CALLBACK(wxgtk_webview_webkit_new_window), this);
464 
465     g_signal_connect_after(m_web_view, "title-changed",
466                            G_CALLBACK(wxgtk_webview_webkit_title_changed), this);
467 
468     g_signal_connect_after(m_web_view, "resource-request-starting",
469                            G_CALLBACK(wxgtk_webview_webkit_resource_req), this);
470 
471 #if WEBKIT_CHECK_VERSION(1, 10, 0)
472      g_signal_connect_after(m_web_view, "context-menu",
473                            G_CALLBACK(wxgtk_webview_webkit_context_menu), this);
474 #endif
475 
476      g_signal_connect_after(m_web_view, "create-web-view",
477                            G_CALLBACK(wxgtk_webview_webkit_create_webview), this);
478 
479     m_parent->DoAddChild( this );
480 
481     PostCreation(size);
482 
483     /* Open a webpage */
484     webkit_web_view_load_uri(m_web_view, url.utf8_str());
485 
486     //Get the initial history limit so we can enable and disable it later
487     WebKitWebBackForwardList* history;
488     history = webkit_web_view_get_back_forward_list(m_web_view);
489     m_historyLimit = webkit_web_back_forward_list_get_limit(history);
490 
491     // last to avoid getting signal too early
492     g_signal_connect_after(m_web_view, "notify::load-status",
493                            G_CALLBACK(wxgtk_webview_webkit_load_status),
494                            this);
495 
496     return true;
497 }
498 
~wxWebViewWebKit()499 wxWebViewWebKit::~wxWebViewWebKit()
500 {
501     if (m_web_view)
502         GTKDisconnect(m_web_view);
503 }
504 
Enable(bool enable)505 bool wxWebViewWebKit::Enable( bool enable )
506 {
507     if (!wxControl::Enable(enable))
508         return false;
509 
510     gtk_widget_set_sensitive(gtk_bin_get_child(GTK_BIN(m_widget)), enable);
511 
512     //if (enable)
513     //    GTKFixSensitivity();
514 
515     return true;
516 }
517 
518 GdkWindow*
GTKGetWindow(wxArrayGdkWindows & WXUNUSED (windows)) const519 wxWebViewWebKit::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
520 {
521     GdkWindow* window = gtk_widget_get_parent_window(m_widget);
522     return window;
523 }
524 
ZoomIn()525 void wxWebViewWebKit::ZoomIn()
526 {
527     webkit_web_view_zoom_in(m_web_view);
528 }
529 
ZoomOut()530 void wxWebViewWebKit::ZoomOut()
531 {
532     webkit_web_view_zoom_out(m_web_view);
533 }
534 
SetWebkitZoom(float level)535 void wxWebViewWebKit::SetWebkitZoom(float level)
536 {
537     webkit_web_view_set_zoom_level(m_web_view, level);
538 }
539 
GetWebkitZoom() const540 float wxWebViewWebKit::GetWebkitZoom() const
541 {
542     return webkit_web_view_get_zoom_level(m_web_view);
543 }
544 
Stop()545 void wxWebViewWebKit::Stop()
546 {
547      webkit_web_view_stop_loading(m_web_view);
548 }
549 
Reload(wxWebViewReloadFlags flags)550 void wxWebViewWebKit::Reload(wxWebViewReloadFlags flags)
551 {
552     if (flags & wxWEBVIEW_RELOAD_NO_CACHE)
553     {
554         webkit_web_view_reload_bypass_cache(m_web_view);
555     }
556     else
557     {
558         webkit_web_view_reload(m_web_view);
559     }
560 }
561 
LoadURL(const wxString & url)562 void wxWebViewWebKit::LoadURL(const wxString& url)
563 {
564     webkit_web_view_load_uri(m_web_view, wxGTK_CONV(url));
565 }
566 
567 
GoBack()568 void wxWebViewWebKit::GoBack()
569 {
570     webkit_web_view_go_back(m_web_view);
571 }
572 
GoForward()573 void wxWebViewWebKit::GoForward()
574 {
575     webkit_web_view_go_forward(m_web_view);
576 }
577 
578 
CanGoBack() const579 bool wxWebViewWebKit::CanGoBack() const
580 {
581     return webkit_web_view_can_go_back(m_web_view) != 0;
582 }
583 
584 
CanGoForward() const585 bool wxWebViewWebKit::CanGoForward() const
586 {
587     return webkit_web_view_can_go_forward(m_web_view) != 0;
588 }
589 
ClearHistory()590 void wxWebViewWebKit::ClearHistory()
591 {
592     WebKitWebBackForwardList* history;
593     history = webkit_web_view_get_back_forward_list(m_web_view);
594     webkit_web_back_forward_list_clear(history);
595 }
596 
EnableHistory(bool enable)597 void wxWebViewWebKit::EnableHistory(bool enable)
598 {
599     WebKitWebBackForwardList* history;
600     history = webkit_web_view_get_back_forward_list(m_web_view);
601     if(enable)
602     {
603         webkit_web_back_forward_list_set_limit(history, m_historyLimit);
604     }
605     else
606     {
607         webkit_web_back_forward_list_set_limit(history, 0);
608     }
609 }
610 
GetBackwardHistory()611 wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewWebKit::GetBackwardHistory()
612 {
613     wxVector<wxSharedPtr<wxWebViewHistoryItem> > backhist;
614     WebKitWebBackForwardList* history;
615     history = webkit_web_view_get_back_forward_list(m_web_view);
616     GList* list = webkit_web_back_forward_list_get_back_list_with_limit(history,
617                                                                         m_historyLimit);
618     //We need to iterate in reverse to get the order we desire
619     for(int i = g_list_length(list) - 1; i >= 0 ; i--)
620     {
621         WebKitWebHistoryItem* gtkitem = (WebKitWebHistoryItem*)g_list_nth_data(list, i);
622         wxWebViewHistoryItem* wxitem = new wxWebViewHistoryItem(
623                                    webkit_web_history_item_get_uri(gtkitem),
624                                    webkit_web_history_item_get_title(gtkitem));
625         wxitem->m_histItem = gtkitem;
626         wxSharedPtr<wxWebViewHistoryItem> item(wxitem);
627         backhist.push_back(item);
628     }
629     return backhist;
630 }
631 
GetForwardHistory()632 wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewWebKit::GetForwardHistory()
633 {
634     wxVector<wxSharedPtr<wxWebViewHistoryItem> > forwardhist;
635     WebKitWebBackForwardList* history;
636     history = webkit_web_view_get_back_forward_list(m_web_view);
637     GList* list = webkit_web_back_forward_list_get_forward_list_with_limit(history,
638                                                                            m_historyLimit);
639     for(guint i = 0; i < g_list_length(list); i++)
640     {
641         WebKitWebHistoryItem* gtkitem = (WebKitWebHistoryItem*)g_list_nth_data(list, i);
642         wxWebViewHistoryItem* wxitem = new wxWebViewHistoryItem(
643                                    webkit_web_history_item_get_uri(gtkitem),
644                                    webkit_web_history_item_get_title(gtkitem));
645         wxitem->m_histItem = gtkitem;
646         wxSharedPtr<wxWebViewHistoryItem> item(wxitem);
647         forwardhist.push_back(item);
648     }
649     return forwardhist;
650 }
651 
LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem> item)652 void wxWebViewWebKit::LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem> item)
653 {
654     WebKitWebHistoryItem* gtkitem = (WebKitWebHistoryItem*)item->m_histItem;
655     if(gtkitem)
656     {
657         webkit_web_view_go_to_back_forward_item(m_web_view,
658                                                 WEBKIT_WEB_HISTORY_ITEM(gtkitem));
659     }
660 }
661 
CanCut() const662 bool wxWebViewWebKit::CanCut() const
663 {
664     return webkit_web_view_can_cut_clipboard(m_web_view) != 0;
665 }
666 
CanCopy() const667 bool wxWebViewWebKit::CanCopy() const
668 {
669     return webkit_web_view_can_copy_clipboard(m_web_view) != 0;
670 }
671 
CanPaste() const672 bool wxWebViewWebKit::CanPaste() const
673 {
674     return webkit_web_view_can_paste_clipboard(m_web_view) != 0;
675 }
676 
Cut()677 void wxWebViewWebKit::Cut()
678 {
679     webkit_web_view_cut_clipboard(m_web_view);
680 }
681 
Copy()682 void wxWebViewWebKit::Copy()
683 {
684     webkit_web_view_copy_clipboard(m_web_view);
685 }
686 
Paste()687 void wxWebViewWebKit::Paste()
688 {
689     webkit_web_view_paste_clipboard(m_web_view);
690 }
691 
CanUndo() const692 bool wxWebViewWebKit::CanUndo() const
693 {
694     return webkit_web_view_can_undo(m_web_view) != 0;
695 }
696 
CanRedo() const697 bool wxWebViewWebKit::CanRedo() const
698 {
699     return webkit_web_view_can_redo(m_web_view) != 0;
700 }
701 
Undo()702 void wxWebViewWebKit::Undo()
703 {
704     webkit_web_view_undo(m_web_view);
705 }
706 
Redo()707 void wxWebViewWebKit::Redo()
708 {
709     webkit_web_view_redo(m_web_view);
710 }
711 
GetCurrentURL() const712 wxString wxWebViewWebKit::GetCurrentURL() const
713 {
714     // FIXME: check which encoding the web kit control uses instead of
715     // assuming UTF8 (here and elsewhere too)
716     return wxString::FromUTF8(webkit_web_view_get_uri(m_web_view));
717 }
718 
719 
GetCurrentTitle() const720 wxString wxWebViewWebKit::GetCurrentTitle() const
721 {
722     return wxString::FromUTF8(webkit_web_view_get_title(m_web_view));
723 }
724 
725 
GetPageSource() const726 wxString wxWebViewWebKit::GetPageSource() const
727 {
728     WebKitWebFrame* frame = webkit_web_view_get_main_frame(m_web_view);
729     WebKitWebDataSource* src = webkit_web_frame_get_data_source (frame);
730 
731     // TODO: check encoding with
732     // const gchar*
733     // webkit_web_data_source_get_encoding(WebKitWebDataSource *data_source);
734     return wxString(webkit_web_data_source_get_data (src)->str, wxConvUTF8);
735 }
736 
737 
GetZoom() const738 wxWebViewZoom wxWebViewWebKit::GetZoom() const
739 {
740     float zoom = GetWebkitZoom();
741 
742     // arbitrary way to map float zoom to our common zoom enum
743     if (zoom <= 0.65)
744     {
745         return wxWEBVIEW_ZOOM_TINY;
746     }
747     if (zoom <= 0.90)
748     {
749         return wxWEBVIEW_ZOOM_SMALL;
750     }
751     if (zoom <= 1.15)
752     {
753         return wxWEBVIEW_ZOOM_MEDIUM;
754     }
755     if (zoom <= 1.45)
756     {
757         return wxWEBVIEW_ZOOM_LARGE;
758     }
759     return wxWEBVIEW_ZOOM_LARGEST;
760 }
761 
762 
SetZoom(wxWebViewZoom zoom)763 void wxWebViewWebKit::SetZoom(wxWebViewZoom zoom)
764 {
765     // arbitrary way to map our common zoom enum to float zoom
766     switch (zoom)
767     {
768         case wxWEBVIEW_ZOOM_TINY:
769             SetWebkitZoom(0.6f);
770             break;
771 
772         case wxWEBVIEW_ZOOM_SMALL:
773             SetWebkitZoom(0.8f);
774             break;
775 
776         case wxWEBVIEW_ZOOM_MEDIUM:
777             SetWebkitZoom(1.0f);
778             break;
779 
780         case wxWEBVIEW_ZOOM_LARGE:
781             SetWebkitZoom(1.3);
782             break;
783 
784         case wxWEBVIEW_ZOOM_LARGEST:
785             SetWebkitZoom(1.6);
786             break;
787 
788         default:
789             wxFAIL;
790     }
791 }
792 
SetZoomType(wxWebViewZoomType type)793 void wxWebViewWebKit::SetZoomType(wxWebViewZoomType type)
794 {
795     webkit_web_view_set_full_content_zoom(m_web_view,
796                                           type == wxWEBVIEW_ZOOM_TYPE_LAYOUT);
797 }
798 
GetZoomType() const799 wxWebViewZoomType wxWebViewWebKit::GetZoomType() const
800 {
801     gboolean fczoom = webkit_web_view_get_full_content_zoom(m_web_view);
802 
803     if (fczoom) return wxWEBVIEW_ZOOM_TYPE_LAYOUT;
804     else        return wxWEBVIEW_ZOOM_TYPE_TEXT;
805 }
806 
CanSetZoomType(wxWebViewZoomType) const807 bool wxWebViewWebKit::CanSetZoomType(wxWebViewZoomType) const
808 {
809     // this port supports all zoom types
810     return true;
811 }
812 
DoSetPage(const wxString & html,const wxString & baseUri)813 void wxWebViewWebKit::DoSetPage(const wxString& html, const wxString& baseUri)
814 {
815     webkit_web_view_load_string (m_web_view,
816                                  html.mb_str(wxConvUTF8),
817                                  "text/html",
818                                  "UTF-8",
819                                  baseUri.mb_str(wxConvUTF8));
820 }
821 
Print()822 void wxWebViewWebKit::Print()
823 {
824     WebKitWebFrame* frame = webkit_web_view_get_main_frame(m_web_view);
825     webkit_web_frame_print (frame);
826 
827     // GtkPrintOperationResult  webkit_web_frame_print_full
828     //      (WebKitWebFrame *frame,
829     //       GtkPrintOperation *operation,
830     //       GtkPrintOperationAction action,
831     //       GError **error);
832 
833 }
834 
835 
IsBusy() const836 bool wxWebViewWebKit::IsBusy() const
837 {
838     return m_busy;
839 
840     // This code looks nice but returns true after a page was cancelled
841     /*
842     WebKitLoadStatus status = webkit_web_view_get_load_status
843             (WEBKIT_WEB_VIEW(web_view));
844 
845 
846 #if WEBKIT_CHECK_VERSION(1,1,16)
847     // WEBKIT_LOAD_FAILED is new in webkit 1.1.16
848     if (status == WEBKIT_LOAD_FAILED)
849     {
850         return false;
851     }
852 #endif
853     if (status == WEBKIT_LOAD_FINISHED)
854     {
855         return false;
856     }
857 
858     return true;
859      */
860 }
861 
SetEditable(bool enable)862 void wxWebViewWebKit::SetEditable(bool enable)
863 {
864     webkit_web_view_set_editable(m_web_view, enable);
865 }
866 
IsEditable() const867 bool wxWebViewWebKit::IsEditable() const
868 {
869     return webkit_web_view_get_editable(m_web_view) != 0;
870 }
871 
DeleteSelection()872 void wxWebViewWebKit::DeleteSelection()
873 {
874     webkit_web_view_delete_selection(m_web_view);
875 }
876 
HasSelection() const877 bool wxWebViewWebKit::HasSelection() const
878 {
879     return webkit_web_view_has_selection(m_web_view) != 0;
880 }
881 
SelectAll()882 void wxWebViewWebKit::SelectAll()
883 {
884     webkit_web_view_select_all(m_web_view);
885 }
886 
GetSelectedText() const887 wxString wxWebViewWebKit::GetSelectedText() const
888 {
889     WebKitDOMDocument* doc;
890     WebKitDOMDOMWindow* win;
891     WebKitDOMDOMSelection* sel;
892     WebKitDOMRange* range;
893 
894     doc = webkit_web_view_get_dom_document(m_web_view);
895     win = webkit_dom_document_get_default_view(WEBKIT_DOM_DOCUMENT(doc));
896     sel = webkit_dom_dom_window_get_selection(WEBKIT_DOM_DOM_WINDOW(win));
897     range = webkit_dom_dom_selection_get_range_at(WEBKIT_DOM_DOM_SELECTION(sel),
898                                                   0, NULL);
899     return wxString(webkit_dom_range_get_text(WEBKIT_DOM_RANGE(range)),
900                     wxConvUTF8);
901 }
902 
GetSelectedSource() const903 wxString wxWebViewWebKit::GetSelectedSource() const
904 {
905     WebKitDOMDocument* doc;
906     WebKitDOMDOMWindow* win;
907     WebKitDOMDOMSelection* sel;
908     WebKitDOMRange* range;
909     WebKitDOMElement* div;
910     WebKitDOMDocumentFragment* clone;
911     WebKitDOMHTMLElement* html;
912 
913     doc = webkit_web_view_get_dom_document(m_web_view);
914     win = webkit_dom_document_get_default_view(WEBKIT_DOM_DOCUMENT(doc));
915     sel = webkit_dom_dom_window_get_selection(WEBKIT_DOM_DOM_WINDOW(win));
916     range = webkit_dom_dom_selection_get_range_at(WEBKIT_DOM_DOM_SELECTION(sel),
917                                                   0, NULL);
918     div = webkit_dom_document_create_element(WEBKIT_DOM_DOCUMENT(doc), "div", NULL);
919 
920     clone = webkit_dom_range_clone_contents(WEBKIT_DOM_RANGE(range), NULL);
921     webkit_dom_node_append_child(&div->parent_instance, &clone->parent_instance, NULL);
922     html = (WebKitDOMHTMLElement*)div;
923 
924     return wxString(webkit_dom_html_element_get_inner_html(WEBKIT_DOM_HTML_ELEMENT(html)),
925                     wxConvUTF8);
926 }
927 
ClearSelection()928 void wxWebViewWebKit::ClearSelection()
929 {
930     WebKitDOMDocument* doc;
931     WebKitDOMDOMWindow* win;
932     WebKitDOMDOMSelection* sel;
933 
934     doc = webkit_web_view_get_dom_document(m_web_view);
935     win = webkit_dom_document_get_default_view(WEBKIT_DOM_DOCUMENT(doc));
936     sel = webkit_dom_dom_window_get_selection(WEBKIT_DOM_DOM_WINDOW(win));
937     webkit_dom_dom_selection_remove_all_ranges(WEBKIT_DOM_DOM_SELECTION(sel));
938 
939 }
940 
GetPageText() const941 wxString wxWebViewWebKit::GetPageText() const
942 {
943     WebKitDOMDocument* doc;
944     WebKitDOMHTMLElement* body;
945 
946     doc = webkit_web_view_get_dom_document(m_web_view);
947     body = webkit_dom_document_get_body(WEBKIT_DOM_DOCUMENT(doc));
948     return wxString(webkit_dom_html_element_get_inner_text(WEBKIT_DOM_HTML_ELEMENT(body)),
949                     wxConvUTF8);
950 }
951 
RunScript(const wxString & javascript)952 void wxWebViewWebKit::RunScript(const wxString& javascript)
953 {
954     webkit_web_view_execute_script(m_web_view,
955                                    javascript.mb_str(wxConvUTF8));
956 }
957 
RegisterHandler(wxSharedPtr<wxWebViewHandler> handler)958 void wxWebViewWebKit::RegisterHandler(wxSharedPtr<wxWebViewHandler> handler)
959 {
960     m_handlerList.push_back(handler);
961 }
962 
EnableContextMenu(bool enable)963 void wxWebViewWebKit::EnableContextMenu(bool enable)
964 {
965 #if !WEBKIT_CHECK_VERSION(1, 10, 0) //If we are using an older version
966     g_object_set(webkit_web_view_get_settings(m_web_view),
967                  "enable-default-context-menu", enable, NULL);
968 #endif
969     wxWebView::EnableContextMenu(enable);
970 }
971 
Find(const wxString & text,int flags)972 long wxWebViewWebKit::Find(const wxString& text, int flags)
973 {
974     bool newSearch = false;
975     if(text != m_findText ||
976        (flags & wxWEBVIEW_FIND_MATCH_CASE) != (m_findFlags & wxWEBVIEW_FIND_MATCH_CASE))
977     {
978         newSearch = true;
979         //If it is a new search we need to clear existing highlights
980         webkit_web_view_unmark_text_matches(m_web_view);
981         webkit_web_view_set_highlight_text_matches(m_web_view, false);
982     }
983 
984     m_findFlags = flags;
985     m_findText = text;
986 
987     //If the search string is empty then we clear any selection and highlight
988     if(text == "")
989     {
990         webkit_web_view_unmark_text_matches(m_web_view);
991         webkit_web_view_set_highlight_text_matches(m_web_view, false);
992         ClearSelection();
993         return wxNOT_FOUND;
994     }
995 
996     bool wrap = false, matchCase = false, forward = true;
997     if(flags & wxWEBVIEW_FIND_WRAP)
998         wrap = true;
999     if(flags & wxWEBVIEW_FIND_MATCH_CASE)
1000         matchCase = true;
1001     if(flags & wxWEBVIEW_FIND_BACKWARDS)
1002         forward = false;
1003 
1004     if(newSearch)
1005     {
1006         //Initially we mark the matches to know how many we have
1007         m_findCount = webkit_web_view_mark_text_matches(m_web_view, wxGTK_CONV(text), matchCase, 0);
1008         //In this case we return early to match IE behaviour
1009         m_findPosition = -1;
1010         return m_findCount;
1011     }
1012     else
1013     {
1014         if(forward)
1015             m_findPosition++;
1016         else
1017             m_findPosition--;
1018         if(m_findPosition < 0)
1019             m_findPosition += m_findCount;
1020         if(m_findPosition > m_findCount)
1021             m_findPosition -= m_findCount;
1022     }
1023 
1024     //Highlight them if needed
1025     bool highlight = flags & wxWEBVIEW_FIND_HIGHLIGHT_RESULT ? true : false;
1026     webkit_web_view_set_highlight_text_matches(m_web_view, highlight);
1027 
1028     if(!webkit_web_view_search_text(m_web_view, wxGTK_CONV(text), matchCase, forward, wrap))
1029     {
1030         m_findPosition = -1;
1031         ClearSelection();
1032         return wxNOT_FOUND;
1033     }
1034     wxLogMessage(wxString::Format("Returning %d", m_findPosition));
1035     return newSearch ? m_findCount : m_findPosition;
1036 }
1037 
FindClear()1038 void wxWebViewWebKit::FindClear()
1039 {
1040     m_findCount = 0;
1041     m_findFlags = 0;
1042     m_findText = "";
1043     m_findPosition = -1;
1044 }
1045 
1046 // static
1047 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))1048 wxWebViewWebKit::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
1049 {
1050      return GetDefaultAttributesFromGTKWidget(webkit_web_view_new());
1051 }
1052 
1053 
1054 #endif // wxUSE_WEBVIEW && wxUSE_WEBVIEW_WEBKIT
1055