1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/webview_webkit2.cpp
3 // Purpose: GTK WebKit2 backend for web view component
4 // Author: Scott Talbert
5 // Copyright: (c) 2017 Scott Talbert
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_WEBKIT2
13
14 #include "wx/dir.h"
15 #include "wx/dynlib.h"
16 #include "wx/filename.h"
17 #include "wx/stdpaths.h"
18 #include "wx/stockitem.h"
19 #include "wx/gtk/webview_webkit.h"
20 #include "wx/gtk/control.h"
21 #include "wx/gtk/private.h"
22 #include "wx/filesys.h"
23 #include "wx/base64.h"
24 #include "wx/log.h"
25 #include "wx/gtk/private/webview_webkit2_extension.h"
26 #include "wx/gtk/private/string.h"
27 #include "wx/gtk/private/webkit.h"
28 #include "wx/gtk/private/error.h"
29 #include "wx/private/jsscriptwrapper.h"
30 #include <webkit2/webkit2.h>
31 #include <JavaScriptCore/JSValueRef.h>
32 #include <JavaScriptCore/JSStringRef.h>
33
34 // Helper function to get string from Webkit JS result
wxGetStringFromJSResult(WebKitJavascriptResult * js_result,wxString * output)35 bool wxGetStringFromJSResult(WebKitJavascriptResult* js_result, wxString* output)
36 {
37 JSGlobalContextRef context = webkit_javascript_result_get_global_context(js_result);
38 JSValueRef value = webkit_javascript_result_get_value(js_result);
39
40 JSValueRef exception = NULL;
41 wxJSStringRef js_value
42 (
43 JSValueIsObject(context, value)
44 ? JSValueCreateJSONString(context, value, 0, &exception)
45 : JSValueToStringCopy(context, value, &exception)
46 );
47
48 if ( exception )
49 {
50 if ( output )
51 {
52 wxJSStringRef ex_value(JSValueToStringCopy(context, exception, NULL));
53 *output = ex_value.ToWxString();
54 }
55
56 return false;
57 }
58
59 if ( output != NULL )
60 *output = js_value.ToWxString();
61
62 return true;
63 }
64
65 // ----------------------------------------------------------------------------
66 // GTK callbacks
67 // ----------------------------------------------------------------------------
68
69 extern "C"
70 {
71
72 static void
wxgtk_webview_webkit_load_changed(GtkWidget *,WebKitLoadEvent load_event,wxWebViewWebKit * webKitCtrl)73 wxgtk_webview_webkit_load_changed(GtkWidget *,
74 WebKitLoadEvent load_event,
75 wxWebViewWebKit *webKitCtrl)
76 {
77 wxString url = webKitCtrl->GetCurrentURL();
78
79 wxString target; // TODO: get target (if possible)
80
81 if (load_event == WEBKIT_LOAD_FINISHED)
82 {
83 webKitCtrl->m_busy = false;
84 wxWebViewEvent event(wxEVT_WEBVIEW_LOADED,
85 webKitCtrl->GetId(),
86 url, target);
87 event.SetEventObject(webKitCtrl);
88
89 webKitCtrl->HandleWindowEvent(event);
90 }
91 else if (load_event == WEBKIT_LOAD_COMMITTED)
92 {
93 webKitCtrl->m_busy = true;
94 wxWebViewEvent event(wxEVT_WEBVIEW_NAVIGATED,
95 webKitCtrl->GetId(),
96 url, target);
97 event.SetEventObject(webKitCtrl);
98
99 webKitCtrl->HandleWindowEvent(event);
100 }
101 }
102
103 static gboolean
wxgtk_webview_webkit_navigation(WebKitWebView *,WebKitPolicyDecision * decision,wxWebViewWebKit * webKitCtrl)104 wxgtk_webview_webkit_navigation(WebKitWebView *,
105 WebKitPolicyDecision *decision,
106 wxWebViewWebKit *webKitCtrl)
107 {
108 WebKitNavigationPolicyDecision* navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION(decision);
109 WebKitNavigationAction* action = webkit_navigation_policy_decision_get_navigation_action(navigation_decision);
110 WebKitURIRequest* request = webkit_navigation_action_get_request(action);
111 const gchar* uri = webkit_uri_request_get_uri(request);
112 wxString target = webkit_navigation_policy_decision_get_frame_name(navigation_decision);
113
114 //If m_creating is true then we are the result of a new window
115 //and so we need to send the event and veto the load
116 if(webKitCtrl->m_creating)
117 {
118 webKitCtrl->m_creating = false;
119 wxWebViewEvent event(wxEVT_WEBVIEW_NEWWINDOW,
120 webKitCtrl->GetId(),
121 wxString(uri, wxConvUTF8),
122 target);
123 event.SetEventObject(webKitCtrl);
124
125 webKitCtrl->HandleWindowEvent(event);
126
127 webkit_policy_decision_ignore(decision);
128 return TRUE;
129 }
130
131 webKitCtrl->m_busy = true;
132
133 wxWebViewEvent event(wxEVT_WEBVIEW_NAVIGATING,
134 webKitCtrl->GetId(),
135 wxString( uri, wxConvUTF8 ),
136 target);
137 event.SetEventObject(webKitCtrl);
138
139 webKitCtrl->HandleWindowEvent(event);
140
141 if (!event.IsAllowed())
142 {
143 webKitCtrl->m_busy = false;
144 webkit_policy_decision_ignore(decision);
145 return TRUE;
146 }
147 else
148 {
149 return FALSE;
150 }
151 }
152
153 static gboolean
wxgtk_webview_webkit_load_failed(WebKitWebView *,WebKitLoadEvent,gchar * uri,GError * error,wxWebViewWebKit * webKitWindow)154 wxgtk_webview_webkit_load_failed(WebKitWebView *,
155 WebKitLoadEvent,
156 gchar *uri,
157 GError *error,
158 wxWebViewWebKit* webKitWindow)
159 {
160 webKitWindow->m_busy = false;
161 wxWebViewNavigationError type = wxWEBVIEW_NAV_ERR_OTHER;
162
163 wxString description(error->message, wxConvUTF8);
164
165 if (strcmp(g_quark_to_string(error->domain), "soup_http_error_quark") == 0)
166 {
167 switch (error->code)
168 {
169 case SOUP_STATUS_CANCELLED:
170 type = wxWEBVIEW_NAV_ERR_USER_CANCELLED;
171 break;
172
173 case SOUP_STATUS_CANT_RESOLVE:
174 case SOUP_STATUS_NOT_FOUND:
175 type = wxWEBVIEW_NAV_ERR_NOT_FOUND;
176 break;
177
178 case SOUP_STATUS_CANT_RESOLVE_PROXY:
179 case SOUP_STATUS_CANT_CONNECT:
180 case SOUP_STATUS_CANT_CONNECT_PROXY:
181 case SOUP_STATUS_SSL_FAILED:
182 case SOUP_STATUS_IO_ERROR:
183 type = wxWEBVIEW_NAV_ERR_CONNECTION;
184 break;
185
186 case SOUP_STATUS_MALFORMED:
187 type = wxWEBVIEW_NAV_ERR_REQUEST;
188 break;
189
190 case SOUP_STATUS_BAD_REQUEST:
191 type = wxWEBVIEW_NAV_ERR_REQUEST;
192 break;
193
194 case SOUP_STATUS_UNAUTHORIZED:
195 case SOUP_STATUS_FORBIDDEN:
196 type = wxWEBVIEW_NAV_ERR_AUTH;
197 break;
198
199 case SOUP_STATUS_METHOD_NOT_ALLOWED:
200 case SOUP_STATUS_NOT_ACCEPTABLE:
201 type = wxWEBVIEW_NAV_ERR_SECURITY;
202 break;
203
204 case SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED:
205 type = wxWEBVIEW_NAV_ERR_AUTH;
206 break;
207
208 case SOUP_STATUS_REQUEST_TIMEOUT:
209 type = wxWEBVIEW_NAV_ERR_CONNECTION;
210 break;
211
212 case SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE:
213 case SOUP_STATUS_REQUEST_URI_TOO_LONG:
214 case SOUP_STATUS_UNSUPPORTED_MEDIA_TYPE:
215 type = wxWEBVIEW_NAV_ERR_REQUEST;
216 break;
217
218 case SOUP_STATUS_BAD_GATEWAY:
219 case SOUP_STATUS_SERVICE_UNAVAILABLE:
220 case SOUP_STATUS_GATEWAY_TIMEOUT:
221 type = wxWEBVIEW_NAV_ERR_CONNECTION;
222 break;
223
224 case SOUP_STATUS_HTTP_VERSION_NOT_SUPPORTED:
225 type = wxWEBVIEW_NAV_ERR_REQUEST;
226 break;
227 }
228 }
229 else if (strcmp(g_quark_to_string(error->domain),
230 "webkit-network-error-quark") == 0)
231 {
232 switch (error->code)
233 {
234 case WEBKIT_NETWORK_ERROR_UNKNOWN_PROTOCOL:
235 type = wxWEBVIEW_NAV_ERR_REQUEST;
236 break;
237
238 case WEBKIT_NETWORK_ERROR_CANCELLED:
239 type = wxWEBVIEW_NAV_ERR_USER_CANCELLED;
240 break;
241
242 case WEBKIT_NETWORK_ERROR_FILE_DOES_NOT_EXIST:
243 type = wxWEBVIEW_NAV_ERR_NOT_FOUND;
244 break;
245 }
246 }
247 else if (strcmp(g_quark_to_string(error->domain),
248 "webkit-policy-error-quark") == 0)
249 {
250 switch (error->code)
251 {
252 case WEBKIT_POLICY_ERROR_CANNOT_USE_RESTRICTED_PORT:
253 type = wxWEBVIEW_NAV_ERR_SECURITY;
254 break;
255 }
256 }
257
258 wxWebViewEvent event(wxEVT_WEBVIEW_ERROR,
259 webKitWindow->GetId(),
260 uri, "");
261 event.SetEventObject(webKitWindow);
262 event.SetString(description);
263 event.SetInt(type);
264
265
266 webKitWindow->HandleWindowEvent(event);
267
268 return FALSE;
269 }
270
271 static gboolean
wxgtk_webview_webkit_new_window(WebKitPolicyDecision * decision,wxWebViewWebKit * webKitCtrl)272 wxgtk_webview_webkit_new_window(WebKitPolicyDecision *decision,
273 wxWebViewWebKit *webKitCtrl)
274 {
275 WebKitNavigationPolicyDecision* navigation_decision = WEBKIT_NAVIGATION_POLICY_DECISION(decision);
276 WebKitNavigationAction* action = webkit_navigation_policy_decision_get_navigation_action(navigation_decision);
277 WebKitURIRequest* request = webkit_navigation_action_get_request(action);
278 const gchar* uri = webkit_uri_request_get_uri(request);
279
280 wxString target = webkit_navigation_policy_decision_get_frame_name(navigation_decision);
281 wxWebViewEvent event(wxEVT_WEBVIEW_NEWWINDOW,
282 webKitCtrl->GetId(),
283 wxString( uri, wxConvUTF8 ),
284 target);
285 event.SetEventObject(webKitCtrl);
286
287 webKitCtrl->HandleWindowEvent(event);
288
289 //We always want the user to handle this themselves
290 webkit_policy_decision_ignore(decision);
291 return TRUE;
292 }
293
294 static gboolean
wxgtk_webview_webkit_enter_fullscreen(WebKitWebView * WXUNUSED (web_view),wxWebViewWebKit * webKitCtrl)295 wxgtk_webview_webkit_enter_fullscreen(WebKitWebView *WXUNUSED(web_view),
296 wxWebViewWebKit *webKitCtrl)
297 {
298 wxWebViewEvent event(wxEVT_WEBVIEW_FULLSCREEN_CHANGED,
299 webKitCtrl->GetId(),
300 wxString(),
301 wxString());
302 event.SetEventObject(webKitCtrl);
303 event.SetInt(1);
304 webKitCtrl->HandleWindowEvent(event);
305
306 return FALSE;
307 }
308
309 static gboolean
wxgtk_webview_webkit_leave_fullscreen(WebKitWebView * WXUNUSED (web_view),wxWebViewWebKit * webKitCtrl)310 wxgtk_webview_webkit_leave_fullscreen(WebKitWebView *WXUNUSED(web_view),
311 wxWebViewWebKit *webKitCtrl)
312 {
313 wxWebViewEvent event(wxEVT_WEBVIEW_FULLSCREEN_CHANGED,
314 webKitCtrl->GetId(),
315 wxString(),
316 wxString());
317 event.SetEventObject(webKitCtrl);
318 event.SetInt(0);
319 webKitCtrl->HandleWindowEvent(event);
320
321 return FALSE;
322 }
323
324 static void
wxgtk_webview_webkit_script_message_received(WebKitUserContentManager * WXUNUSED (content_manager),WebKitJavascriptResult * js_result,wxWebViewWebKit * webKitCtrl)325 wxgtk_webview_webkit_script_message_received(WebKitUserContentManager *WXUNUSED(content_manager),
326 WebKitJavascriptResult *js_result,
327 wxWebViewWebKit *webKitCtrl)
328 {
329 wxWebViewEvent event(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED,
330 webKitCtrl->GetId(),
331 webKitCtrl->GetCurrentURL(),
332 "");
333 wxString msgStr;
334 if (wxGetStringFromJSResult(js_result, &msgStr))
335 event.SetString(msgStr);
336 webKitCtrl->HandleWindowEvent(event);
337 }
338
339 static gboolean
wxgtk_webview_webkit_decide_policy(WebKitWebView * web_view,WebKitPolicyDecision * decision,WebKitPolicyDecisionType type,wxWebViewWebKit * webKitCtrl)340 wxgtk_webview_webkit_decide_policy(WebKitWebView *web_view,
341 WebKitPolicyDecision *decision,
342 WebKitPolicyDecisionType type,
343 wxWebViewWebKit *webKitCtrl)
344 {
345 switch (type)
346 {
347 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
348 return wxgtk_webview_webkit_navigation(web_view, decision, webKitCtrl);
349 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
350 return wxgtk_webview_webkit_new_window(decision, webKitCtrl);
351 default:
352 return FALSE;
353 }
354 }
355
356 static void
wxgtk_webview_webkit_title_changed(GtkWidget * widget,GParamSpec *,wxWebViewWebKit * webKitCtrl)357 wxgtk_webview_webkit_title_changed(GtkWidget* widget,
358 GParamSpec *,
359 wxWebViewWebKit *webKitCtrl)
360 {
361 gchar *title;
362 g_object_get(G_OBJECT(widget), "title", &title, NULL);
363
364 wxWebViewEvent event(wxEVT_WEBVIEW_TITLE_CHANGED,
365 webKitCtrl->GetId(),
366 webKitCtrl->GetCurrentURL(),
367 "");
368 event.SetEventObject(webKitCtrl);
369 event.SetString(wxString(title, wxConvUTF8));
370
371 webKitCtrl->HandleWindowEvent(event);
372
373 g_free(title);
374 }
375
376 static void
wxgtk_webview_webkit_uri_scheme_request_cb(WebKitURISchemeRequest * request,wxWebViewWebKit * webKitCtrl)377 wxgtk_webview_webkit_uri_scheme_request_cb(WebKitURISchemeRequest *request,
378 wxWebViewWebKit *webKitCtrl)
379 {
380 const wxString scheme = wxString::FromUTF8(webkit_uri_scheme_request_get_scheme(request));
381
382 wxSharedPtr<wxWebViewHandler> handler;
383 wxVector<wxSharedPtr<wxWebViewHandler> > handlers = webKitCtrl->GetHandlers();
384
385 for(wxVector<wxSharedPtr<wxWebViewHandler> >::iterator it = handlers.begin();
386 it != handlers.end(); ++it)
387 {
388 if(scheme == (*it)->GetName())
389 {
390 handler = (*it);
391 }
392 }
393
394 if(handler)
395 {
396 const wxString uri = wxString::FromUTF8(webkit_uri_scheme_request_get_uri(request));
397
398 wxFSFile* file = handler->GetFile(uri);
399 if(file)
400 {
401 gint64 length = file->GetStream()->GetLength();
402 guint8 *data = g_new(guint8, length);
403 file->GetStream()->Read(data, length);
404 GInputStream *stream = g_memory_input_stream_new_from_data(data,
405 length,
406 g_free);
407 wxString mime = file->GetMimeType();
408 webkit_uri_scheme_request_finish(request, stream, length, mime.utf8_str());
409 }
410 else
411 {
412 wxGtkError error(g_error_new(WEBKIT_NETWORK_ERROR,
413 WEBKIT_NETWORK_ERROR_FILE_DOES_NOT_EXIST,
414 "File not found: %s", uri.utf8_str().data()));
415 webkit_uri_scheme_request_finish_error(request, error);
416 }
417 }
418 else
419 {
420 wxGtkError error(g_error_new(WEBKIT_NETWORK_ERROR,
421 WEBKIT_NETWORK_ERROR_UNKNOWN_PROTOCOL,
422 "Unknown scheme: %s", scheme.utf8_str().data()));
423 webkit_uri_scheme_request_finish_error(request, error);
424 }
425 }
426
427 static gboolean
wxgtk_webview_webkit_context_menu(WebKitWebView *,WebKitContextMenu *,GdkEvent *,WebKitHitTestResult *,wxWebViewWebKit * webKitCtrl)428 wxgtk_webview_webkit_context_menu(WebKitWebView *,
429 WebKitContextMenu *,
430 GdkEvent *,
431 WebKitHitTestResult *,
432 wxWebViewWebKit *webKitCtrl)
433 {
434 return !webKitCtrl->IsContextMenuEnabled();
435 }
436
437 static WebKitWebView*
wxgtk_webview_webkit_create_webview(WebKitWebView * web_view,WebKitNavigationAction *,wxWebViewWebKit * webKitCtrl)438 wxgtk_webview_webkit_create_webview(WebKitWebView *web_view,
439 WebKitNavigationAction *,
440 wxWebViewWebKit *webKitCtrl)
441 {
442 //As we do not know the uri being loaded at this point allow the load to
443 //continue and catch it in navigation-policy-decision-requested
444 webKitCtrl->m_creating = true;
445 return web_view;
446 }
447
448 static void
wxgtk_webview_webkit_counted_matches(WebKitFindController *,guint match_count,int * findCount)449 wxgtk_webview_webkit_counted_matches(WebKitFindController *,
450 guint match_count,
451 int *findCount)
452 {
453 *findCount = match_count;
454 }
455
456 // This function checks if the specified directory contains our web extension.
CheckDirectoryForWebExt(const wxString & dirname)457 static bool CheckDirectoryForWebExt(const wxString& dirname)
458 {
459 wxDir dir;
460 if ( !wxDir::Exists(dirname) || !dir.Open(dirname) )
461 return false;
462
463 wxString file;
464 bool cont = dir.GetFirst
465 (
466 &file,
467 "webkit2_ext*" + wxDynamicLibrary::GetDllExt(wxDL_MODULE),
468 wxDIR_FILES
469 );
470 while ( cont )
471 {
472 wxDynamicLibrary dl;
473 if ( dl.Load(wxFileName(dirname, file).GetFullPath(),
474 wxDL_VERBATIM | wxDL_LAZY) &&
475 dl.HasSymbol("webkit_web_extension_initialize_with_user_data") )
476 {
477 // Looks like our extension.
478 return true;
479 }
480
481 cont = dir.GetNext(&file);
482 }
483
484 return false;
485 }
486
TrySetWebExtensionsDirectory(WebKitWebContext * context,const wxString & dir)487 static bool TrySetWebExtensionsDirectory(WebKitWebContext *context, const wxString& dir)
488 {
489 if (dir.empty() || !CheckDirectoryForWebExt(dir))
490 return false;
491
492 webkit_web_context_set_web_extensions_directory(context, dir.utf8_str());
493 return true;
494 }
495
GetStandardWebExtensionsDir()496 static wxString GetStandardWebExtensionsDir()
497 {
498 wxString dir = wxDynamicLibrary::GetPluginsDirectory();
499 if ( !dir.empty() )
500 dir += "/web-extensions";
501 return dir;
502 }
503
504 static void
wxgtk_initialize_web_extensions(WebKitWebContext * context,GDBusServer * dbusServer)505 wxgtk_initialize_web_extensions(WebKitWebContext *context,
506 GDBusServer *dbusServer)
507 {
508 const char *address = g_dbus_server_get_client_address(dbusServer);
509 GVariant *user_data = g_variant_new("(s)", address);
510
511 // Try to setup extension loading from the location it is supposed to be
512 // normally installed in.
513 if ( !TrySetWebExtensionsDirectory(context, GetStandardWebExtensionsDir()) )
514 {
515 // These relative locations are used as fallbacks to allow running
516 // the tests and sample using wxWebView before installing it.
517 wxString exepath = wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath();
518 if ( !exepath.empty() )
519 {
520 wxString const directories[] =
521 {
522 exepath + "/..",
523 exepath + "/../..",
524 exepath + "/lib",
525 };
526
527 for ( size_t n = 0; n < WXSIZEOF(directories); ++n )
528 {
529 if ( !TrySetWebExtensionsDirectory(context, directories[n]) )
530 break;
531 }
532 }
533 }
534
535 webkit_web_context_set_web_extensions_initialization_user_data(context,
536 user_data);
537 }
538
539 static gboolean
wxgtk_new_connection_cb(GDBusServer *,GDBusConnection * connection,GDBusProxy ** proxy)540 wxgtk_new_connection_cb(GDBusServer *,
541 GDBusConnection *connection,
542 GDBusProxy **proxy)
543 {
544 GError *error = NULL;
545 GDBusProxyFlags flags = GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS);
546 *proxy = g_dbus_proxy_new_sync(connection,
547 flags,
548 NULL,
549 NULL,
550 WXGTK_WEB_EXTENSION_OBJECT_PATH,
551 WXGTK_WEB_EXTENSION_INTERFACE,
552 NULL,
553 &error);
554 if (error)
555 {
556 g_warning("Failed to create dbus proxy: %s", error->message);
557 g_error_free(error);
558 }
559
560 return TRUE;
561 }
562
563 static
564 gboolean
wxgtk_dbus_peer_is_authorized(GCredentials * peer_credentials)565 wxgtk_dbus_peer_is_authorized(GCredentials *peer_credentials)
566 {
567 static GCredentials *own_credentials = g_credentials_new();
568 GError *error = NULL;
569
570 if (peer_credentials && g_credentials_is_same_user(peer_credentials, own_credentials, &error))
571 {
572 return TRUE;
573 }
574
575 if (error)
576 {
577 g_warning("Failed to authorize web extension connection: %s", error->message);
578 g_error_free(error);
579 }
580 return FALSE;
581 }
582
583 static gboolean
wxgtk_authorize_authenticated_peer_cb(GDBusAuthObserver *,GIOStream *,GCredentials * credentials,wxWebViewWebKit *)584 wxgtk_authorize_authenticated_peer_cb(GDBusAuthObserver *,
585 GIOStream *,
586 GCredentials *credentials,
587 wxWebViewWebKit *)
588 {
589 return wxgtk_dbus_peer_is_authorized(credentials);
590 }
591
592 } // extern "C"
593
594 //-----------------------------------------------------------------------------
595 // wxWebViewFactoryWebKit
596 //-----------------------------------------------------------------------------
597
GetVersionInfo()598 wxVersionInfo wxWebViewFactoryWebKit::GetVersionInfo()
599 {
600 return wxVersionInfo("webkit2", webkit_get_major_version(),
601 webkit_get_minor_version(), webkit_get_micro_version());
602 }
603
604 //-----------------------------------------------------------------------------
605 // wxWebViewWebKit
606 //-----------------------------------------------------------------------------
607
608 wxIMPLEMENT_DYNAMIC_CLASS(wxWebViewWebKit, wxWebView);
609
wxWebViewWebKit()610 wxWebViewWebKit::wxWebViewWebKit()
611 {
612 m_web_view = NULL;
613 m_dbusServer = NULL;
614 m_extension = NULL;
615 }
616
Create(wxWindow * parent,wxWindowID id,const wxString & url,const wxPoint & pos,const wxSize & size,long style,const wxString & name)617 bool wxWebViewWebKit::Create(wxWindow *parent,
618 wxWindowID id,
619 const wxString &url,
620 const wxPoint& pos,
621 const wxSize& size,
622 long style,
623 const wxString& name)
624 {
625 m_web_view = NULL;
626 m_dbusServer = NULL;
627 m_extension = NULL;
628 m_busy = false;
629 m_guard = false;
630 m_creating = false;
631 FindClear();
632
633 // We currently unconditionally impose scrolling in both directions as it's
634 // necessary to show arbitrary pages.
635 style |= wxHSCROLL | wxVSCROLL;
636
637 if (!PreCreation( parent, pos, size ) ||
638 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
639 {
640 wxFAIL_MSG( wxT("wxWebViewWebKit creation failed") );
641 return false;
642 }
643
644 SetupWebExtensionServer();
645 g_signal_connect(webkit_web_context_get_default(),
646 "initialize-web-extensions",
647 G_CALLBACK(wxgtk_initialize_web_extensions),
648 m_dbusServer);
649
650 m_web_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
651 GTKCreateScrolledWindowWith(GTK_WIDGET(m_web_view));
652 g_object_ref(m_widget);
653
654 if (!m_customUserAgent.empty())
655 SetUserAgent(m_customUserAgent);
656
657 g_signal_connect(m_web_view, "decide-policy",
658 G_CALLBACK(wxgtk_webview_webkit_decide_policy),
659 this);
660
661 g_signal_connect(m_web_view, "load-failed",
662 G_CALLBACK(wxgtk_webview_webkit_load_failed), this);
663
664 g_signal_connect(m_web_view, "notify::title",
665 G_CALLBACK(wxgtk_webview_webkit_title_changed), this);
666
667 g_signal_connect(m_web_view, "context-menu",
668 G_CALLBACK(wxgtk_webview_webkit_context_menu), this);
669
670 g_signal_connect(m_web_view, "create",
671 G_CALLBACK(wxgtk_webview_webkit_create_webview), this);
672
673 g_signal_connect(m_web_view, "enter-fullscreen",
674 G_CALLBACK(wxgtk_webview_webkit_enter_fullscreen), this);
675
676 g_signal_connect(m_web_view, "leave-fullscreen",
677 G_CALLBACK(wxgtk_webview_webkit_leave_fullscreen), this);
678
679 WebKitFindController* findctrl = webkit_web_view_get_find_controller(m_web_view);
680 g_signal_connect(findctrl, "counted-matches",
681 G_CALLBACK(wxgtk_webview_webkit_counted_matches),
682 &m_findCount);
683
684 m_parent->DoAddChild( this );
685
686 PostCreation(size);
687
688 /* Open a webpage */
689 webkit_web_view_load_uri(m_web_view, url.utf8_str());
690
691 // last to avoid getting signal too early
692 g_signal_connect(m_web_view, "load-changed",
693 G_CALLBACK(wxgtk_webview_webkit_load_changed),
694 this);
695
696 return true;
697 }
698
~wxWebViewWebKit()699 wxWebViewWebKit::~wxWebViewWebKit()
700 {
701 if (m_web_view)
702 GTKDisconnect(m_web_view);
703 if (m_dbusServer)
704 {
705 g_dbus_server_stop(m_dbusServer);
706 g_signal_handlers_disconnect_by_data(
707 webkit_web_context_get_default(), m_dbusServer);
708 }
709 g_clear_object(&m_dbusServer);
710 g_clear_object(&m_extension);
711 }
712
Enable(bool enable)713 bool wxWebViewWebKit::Enable( bool enable )
714 {
715 if (!wxControl::Enable(enable))
716 return false;
717
718 gtk_widget_set_sensitive(gtk_bin_get_child(GTK_BIN(m_widget)), enable);
719
720 return true;
721 }
722
723 GdkWindow*
GTKGetWindow(wxArrayGdkWindows & WXUNUSED (windows)) const724 wxWebViewWebKit::GTKGetWindow(wxArrayGdkWindows& WXUNUSED(windows)) const
725 {
726 GdkWindow* window = gtk_widget_get_parent_window(m_widget);
727 return window;
728 }
729
ZoomIn()730 void wxWebViewWebKit::ZoomIn()
731 {
732 SetWebkitZoom(GetWebkitZoom() + 0.1f);
733 }
734
ZoomOut()735 void wxWebViewWebKit::ZoomOut()
736 {
737 SetWebkitZoom(GetWebkitZoom() - 0.1f);
738 }
739
SetWebkitZoom(float level)740 void wxWebViewWebKit::SetWebkitZoom(float level)
741 {
742 webkit_web_view_set_zoom_level(m_web_view, double(level));
743 }
744
GetWebkitZoom() const745 float wxWebViewWebKit::GetWebkitZoom() const
746 {
747 return webkit_web_view_get_zoom_level(m_web_view);
748 }
749
EnableAccessToDevTools(bool enable)750 void wxWebViewWebKit::EnableAccessToDevTools(bool enable)
751 {
752 WebKitSettings* settings = webkit_web_view_get_settings(m_web_view);
753 webkit_settings_set_enable_developer_extras(settings, enable);
754 }
755
IsAccessToDevToolsEnabled() const756 bool wxWebViewWebKit::IsAccessToDevToolsEnabled() const
757 {
758 WebKitSettings* settings = webkit_web_view_get_settings(m_web_view);
759 return webkit_settings_get_enable_developer_extras(settings);
760 }
761
SetUserAgent(const wxString & userAgent)762 bool wxWebViewWebKit::SetUserAgent(const wxString& userAgent)
763 {
764 if (m_web_view)
765 {
766 WebKitSettings* settings = webkit_web_view_get_settings(m_web_view);
767 webkit_settings_set_user_agent(settings, userAgent.utf8_str());
768 }
769 else
770 m_customUserAgent = userAgent;
771 return true;
772 }
773
Stop()774 void wxWebViewWebKit::Stop()
775 {
776 webkit_web_view_stop_loading(m_web_view);
777 }
778
Reload(wxWebViewReloadFlags flags)779 void wxWebViewWebKit::Reload(wxWebViewReloadFlags flags)
780 {
781 if (flags & wxWEBVIEW_RELOAD_NO_CACHE)
782 {
783 webkit_web_view_reload_bypass_cache(m_web_view);
784 }
785 else
786 {
787 webkit_web_view_reload(m_web_view);
788 }
789 }
790
LoadURL(const wxString & url)791 void wxWebViewWebKit::LoadURL(const wxString& url)
792 {
793 webkit_web_view_load_uri(m_web_view, wxGTK_CONV(url));
794 }
795
796
GoBack()797 void wxWebViewWebKit::GoBack()
798 {
799 webkit_web_view_go_back(m_web_view);
800 }
801
GoForward()802 void wxWebViewWebKit::GoForward()
803 {
804 webkit_web_view_go_forward(m_web_view);
805 }
806
807
CanGoBack() const808 bool wxWebViewWebKit::CanGoBack() const
809 {
810 return webkit_web_view_can_go_back(m_web_view) != 0;
811 }
812
813
CanGoForward() const814 bool wxWebViewWebKit::CanGoForward() const
815 {
816 return webkit_web_view_can_go_forward(m_web_view) != 0;
817 }
818
ClearHistory()819 void wxWebViewWebKit::ClearHistory()
820 {
821 // In WebKit2GTK+, the BackForwardList can't be cleared so do nothing.
822 }
823
EnableHistory(bool)824 void wxWebViewWebKit::EnableHistory(bool)
825 {
826 // In WebKit2GTK+, history can't be disabled so do nothing here.
827 }
828
GetBackwardHistory()829 wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewWebKit::GetBackwardHistory()
830 {
831 wxVector<wxSharedPtr<wxWebViewHistoryItem> > backhist;
832 WebKitBackForwardList* history =
833 webkit_web_view_get_back_forward_list(m_web_view);
834 GList* list = webkit_back_forward_list_get_back_list(history);
835 //We need to iterate in reverse to get the order we desire
836 for(int i = g_list_length(list) - 1; i >= 0 ; i--)
837 {
838 WebKitBackForwardListItem* gtkitem = (WebKitBackForwardListItem*)g_list_nth_data(list, i);
839 wxWebViewHistoryItem* wxitem = new wxWebViewHistoryItem(
840 webkit_back_forward_list_item_get_uri(gtkitem),
841 webkit_back_forward_list_item_get_title(gtkitem));
842 wxitem->m_histItem = gtkitem;
843 wxSharedPtr<wxWebViewHistoryItem> item(wxitem);
844 backhist.push_back(item);
845 }
846 return backhist;
847 }
848
GetForwardHistory()849 wxVector<wxSharedPtr<wxWebViewHistoryItem> > wxWebViewWebKit::GetForwardHistory()
850 {
851 wxVector<wxSharedPtr<wxWebViewHistoryItem> > forwardhist;
852 WebKitBackForwardList* history =
853 webkit_web_view_get_back_forward_list(m_web_view);
854 GList* list = webkit_back_forward_list_get_forward_list(history);
855 for(guint i = 0; i < g_list_length(list); i++)
856 {
857 WebKitBackForwardListItem* gtkitem = (WebKitBackForwardListItem*)g_list_nth_data(list, i);
858 wxWebViewHistoryItem* wxitem = new wxWebViewHistoryItem(
859 webkit_back_forward_list_item_get_uri(gtkitem),
860 webkit_back_forward_list_item_get_title(gtkitem));
861 wxitem->m_histItem = gtkitem;
862 wxSharedPtr<wxWebViewHistoryItem> item(wxitem);
863 forwardhist.push_back(item);
864 }
865 return forwardhist;
866 }
867
LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem> item)868 void wxWebViewWebKit::LoadHistoryItem(wxSharedPtr<wxWebViewHistoryItem> item)
869 {
870 WebKitBackForwardListItem* gtkitem = (WebKitBackForwardListItem*)item->m_histItem;
871 if(gtkitem)
872 {
873 webkit_web_view_go_to_back_forward_list_item(m_web_view,
874 WEBKIT_BACK_FORWARD_LIST_ITEM(gtkitem));
875 }
876 }
877
878 extern "C" {
wxgtk_can_execute_editing_command_cb(GObject *,GAsyncResult * res,void * user_data)879 static void wxgtk_can_execute_editing_command_cb(GObject*,
880 GAsyncResult *res,
881 void* user_data)
882 {
883 GAsyncResult** res_out = static_cast<GAsyncResult**>(user_data);
884 g_object_ref(res);
885 *res_out = res;
886 }
887 }
888
CanExecuteEditingCommand(const gchar * command) const889 bool wxWebViewWebKit::CanExecuteEditingCommand(const gchar* command) const
890 {
891 GAsyncResult *result = NULL;
892 webkit_web_view_can_execute_editing_command(m_web_view,
893 command,
894 NULL,
895 wxgtk_can_execute_editing_command_cb,
896 &result);
897
898 GMainContext *main_context = g_main_context_get_thread_default();
899 while (!result)
900 {
901 g_main_context_iteration(main_context, TRUE);
902 }
903
904 gboolean can_execute = webkit_web_view_can_execute_editing_command_finish(m_web_view,
905 result,
906 NULL);
907 g_object_unref(result);
908
909 return can_execute != 0;
910 }
911
CanCut() const912 bool wxWebViewWebKit::CanCut() const
913 {
914 return CanExecuteEditingCommand(WEBKIT_EDITING_COMMAND_CUT);
915 }
916
CanCopy() const917 bool wxWebViewWebKit::CanCopy() const
918 {
919 return CanExecuteEditingCommand(WEBKIT_EDITING_COMMAND_COPY);
920 }
921
CanPaste() const922 bool wxWebViewWebKit::CanPaste() const
923 {
924 return CanExecuteEditingCommand(WEBKIT_EDITING_COMMAND_PASTE);
925 }
926
Cut()927 void wxWebViewWebKit::Cut()
928 {
929 webkit_web_view_execute_editing_command(m_web_view,
930 WEBKIT_EDITING_COMMAND_CUT);
931 }
932
Copy()933 void wxWebViewWebKit::Copy()
934 {
935 webkit_web_view_execute_editing_command(m_web_view,
936 WEBKIT_EDITING_COMMAND_COPY);
937 }
938
Paste()939 void wxWebViewWebKit::Paste()
940 {
941 webkit_web_view_execute_editing_command(m_web_view,
942 WEBKIT_EDITING_COMMAND_PASTE);
943 }
944
CanUndo() const945 bool wxWebViewWebKit::CanUndo() const
946 {
947 return CanExecuteEditingCommand(WEBKIT_EDITING_COMMAND_UNDO);
948 }
949
CanRedo() const950 bool wxWebViewWebKit::CanRedo() const
951 {
952 return CanExecuteEditingCommand(WEBKIT_EDITING_COMMAND_REDO);
953 }
954
Undo()955 void wxWebViewWebKit::Undo()
956 {
957 webkit_web_view_execute_editing_command(m_web_view,
958 WEBKIT_EDITING_COMMAND_UNDO);
959 }
960
Redo()961 void wxWebViewWebKit::Redo()
962 {
963 webkit_web_view_execute_editing_command(m_web_view,
964 WEBKIT_EDITING_COMMAND_REDO);
965 }
966
GetCurrentURL() const967 wxString wxWebViewWebKit::GetCurrentURL() const
968 {
969 // FIXME: check which encoding the web kit control uses instead of
970 // assuming UTF8 (here and elsewhere too)
971 return wxString::FromUTF8(webkit_web_view_get_uri(m_web_view));
972 }
973
974
GetCurrentTitle() const975 wxString wxWebViewWebKit::GetCurrentTitle() const
976 {
977 return wxString::FromUTF8(webkit_web_view_get_title(m_web_view));
978 }
979
980
981 extern "C" {
wxgtk_web_resource_get_data_cb(GObject *,GAsyncResult * res,void * user_data)982 static void wxgtk_web_resource_get_data_cb(GObject*,
983 GAsyncResult *res,
984 void* user_data)
985 {
986 GAsyncResult** res_out = static_cast<GAsyncResult**>(user_data);
987 g_object_ref(res);
988 *res_out = res;
989 }
990 }
991
GetPageSource() const992 wxString wxWebViewWebKit::GetPageSource() const
993 {
994 WebKitWebResource *resource = webkit_web_view_get_main_resource(m_web_view);
995 if (!resource)
996 {
997 return wxString();
998 }
999
1000 GAsyncResult *result = NULL;
1001 webkit_web_resource_get_data(resource, NULL,
1002 wxgtk_web_resource_get_data_cb,
1003 &result);
1004
1005 GMainContext *main_context = g_main_context_get_thread_default();
1006 while (!result)
1007 {
1008 g_main_context_iteration(main_context, TRUE);
1009 }
1010
1011 size_t length;
1012 guchar *source = webkit_web_resource_get_data_finish(resource, result,
1013 &length, NULL);
1014 if (result)
1015 {
1016 g_object_unref(result);
1017 }
1018
1019 if (source)
1020 {
1021 wxString wxs(source, wxConvUTF8, length);
1022 free(source);
1023 return wxs;
1024 }
1025 return wxString();
1026 }
1027
1028
GetZoomFactor() const1029 float wxWebViewWebKit::GetZoomFactor() const
1030 {
1031 return GetWebkitZoom();
1032 }
1033
SetZoomFactor(float zoom)1034 void wxWebViewWebKit::SetZoomFactor(float zoom)
1035 {
1036 SetWebkitZoom(zoom);
1037 }
1038
SetZoomType(wxWebViewZoomType type)1039 void wxWebViewWebKit::SetZoomType(wxWebViewZoomType type)
1040 {
1041 WebKitSettings* settings = webkit_web_view_get_settings(m_web_view);
1042 webkit_settings_set_zoom_text_only(settings,
1043 type == wxWEBVIEW_ZOOM_TYPE_TEXT);
1044 }
1045
GetZoomType() const1046 wxWebViewZoomType wxWebViewWebKit::GetZoomType() const
1047 {
1048 WebKitSettings* settings = webkit_web_view_get_settings(m_web_view);
1049 gboolean tozoom = webkit_settings_get_zoom_text_only(settings);
1050
1051 if (tozoom)
1052 return wxWEBVIEW_ZOOM_TYPE_TEXT;
1053 else
1054 return wxWEBVIEW_ZOOM_TYPE_LAYOUT;
1055 }
1056
CanSetZoomType(wxWebViewZoomType) const1057 bool wxWebViewWebKit::CanSetZoomType(wxWebViewZoomType) const
1058 {
1059 // this port supports all zoom types
1060 return true;
1061 }
1062
DoSetPage(const wxString & html,const wxString & baseUri)1063 void wxWebViewWebKit::DoSetPage(const wxString& html, const wxString& baseUri)
1064 {
1065 webkit_web_view_load_html(m_web_view,
1066 html.mb_str(wxConvUTF8),
1067 baseUri.mb_str(wxConvUTF8));
1068 }
1069
Print()1070 void wxWebViewWebKit::Print()
1071 {
1072 WebKitPrintOperation* printop = webkit_print_operation_new(m_web_view);
1073 webkit_print_operation_run_dialog(printop, NULL);
1074 g_object_unref(printop);
1075 }
1076
1077
IsBusy() const1078 bool wxWebViewWebKit::IsBusy() const
1079 {
1080 return m_busy;
1081 }
1082
SetEditable(bool enable)1083 void wxWebViewWebKit::SetEditable(bool enable)
1084 {
1085 #if WEBKIT_CHECK_VERSION(2, 8, 0)
1086 webkit_web_view_set_editable(m_web_view, enable);
1087 #else
1088 // Not supported in older versions.
1089 wxUnusedVar(enable);
1090 #endif
1091 }
1092
IsEditable() const1093 bool wxWebViewWebKit::IsEditable() const
1094 {
1095 #if WEBKIT_CHECK_VERSION(2, 8, 0)
1096 gboolean editable;
1097 g_object_get(m_web_view, "editable", &editable, NULL);
1098 return editable != 0;
1099 #else
1100 return false;
1101 #endif
1102 }
1103
DeleteSelection()1104 void wxWebViewWebKit::DeleteSelection()
1105 {
1106 GDBusProxy *extension = GetExtensionProxy();
1107 if (extension)
1108 {
1109 guint64 page_id = webkit_web_view_get_page_id(m_web_view);
1110 GVariant *retval = g_dbus_proxy_call_sync(extension,
1111 "DeleteSelection",
1112 g_variant_new("(t)", page_id),
1113 G_DBUS_CALL_FLAGS_NONE, -1,
1114 NULL, NULL);
1115 if (retval)
1116 {
1117 g_variant_unref(retval);
1118 }
1119 }
1120 }
1121
HasSelection() const1122 bool wxWebViewWebKit::HasSelection() const
1123 {
1124 GDBusProxy *extension = GetExtensionProxy();
1125 if (extension)
1126 {
1127 guint64 page_id = webkit_web_view_get_page_id(m_web_view);
1128 GVariant *retval = g_dbus_proxy_call_sync(extension,
1129 "HasSelection",
1130 g_variant_new("(t)", page_id),
1131 G_DBUS_CALL_FLAGS_NONE, -1,
1132 NULL, NULL);
1133 if (retval)
1134 {
1135 gboolean has_selection = FALSE;
1136 g_variant_get(retval, "(b)", &has_selection);
1137 g_variant_unref(retval);
1138 return has_selection != 0;
1139 }
1140 }
1141 return false;
1142 }
1143
SelectAll()1144 void wxWebViewWebKit::SelectAll()
1145 {
1146 webkit_web_view_execute_editing_command(m_web_view,
1147 WEBKIT_EDITING_COMMAND_SELECT_ALL);
1148 }
1149
GetSelectedText() const1150 wxString wxWebViewWebKit::GetSelectedText() const
1151 {
1152 GDBusProxy *extension = GetExtensionProxy();
1153 if (extension)
1154 {
1155 guint64 page_id = webkit_web_view_get_page_id(m_web_view);
1156 GVariant *retval = g_dbus_proxy_call_sync(extension,
1157 "GetSelectedText",
1158 g_variant_new("(t)", page_id),
1159 G_DBUS_CALL_FLAGS_NONE, -1,
1160 NULL, NULL);
1161 if (retval)
1162 {
1163 char *text;
1164 g_variant_get(retval, "(s)", &text);
1165 g_variant_unref(retval);
1166 return wxString(text, wxConvUTF8);
1167 }
1168 }
1169 return wxString();
1170 }
1171
GetSelectedSource() const1172 wxString wxWebViewWebKit::GetSelectedSource() const
1173 {
1174 GDBusProxy *extension = GetExtensionProxy();
1175 if (extension)
1176 {
1177 guint64 page_id = webkit_web_view_get_page_id(m_web_view);
1178 GVariant *retval = g_dbus_proxy_call_sync(extension,
1179 "GetSelectedSource",
1180 g_variant_new("(t)", page_id),
1181 G_DBUS_CALL_FLAGS_NONE, -1,
1182 NULL, NULL);
1183 if (retval)
1184 {
1185 char *source;
1186 g_variant_get(retval, "(s)", &source);
1187 g_variant_unref(retval);
1188 return wxString(source, wxConvUTF8);
1189 }
1190 }
1191 return wxString();
1192 }
1193
ClearSelection()1194 void wxWebViewWebKit::ClearSelection()
1195 {
1196 GDBusProxy *extension = GetExtensionProxy();
1197 if (extension)
1198 {
1199 guint64 page_id = webkit_web_view_get_page_id(m_web_view);
1200 GVariant *retval = g_dbus_proxy_call_sync(extension,
1201 "ClearSelection",
1202 g_variant_new("(t)", page_id),
1203 G_DBUS_CALL_FLAGS_NONE, -1,
1204 NULL, NULL);
1205 if (retval)
1206 {
1207 g_variant_unref(retval);
1208 }
1209 }
1210 }
1211
GetPageText() const1212 wxString wxWebViewWebKit::GetPageText() const
1213 {
1214 GDBusProxy *extension = GetExtensionProxy();
1215 if (extension)
1216 {
1217 guint64 page_id = webkit_web_view_get_page_id(m_web_view);
1218 GVariant *retval = g_dbus_proxy_call_sync(extension,
1219 "GetPageText",
1220 g_variant_new("(t)", page_id),
1221 G_DBUS_CALL_FLAGS_NONE, -1,
1222 NULL, NULL);
1223 if (retval)
1224 {
1225 char *text;
1226 g_variant_get(retval, "(s)", &text);
1227 g_variant_unref(retval);
1228 return wxString(text, wxConvUTF8);
1229 }
1230 }
1231 return wxString();
1232 }
1233
1234 extern "C"
1235 {
1236
wxgtk_run_javascript_cb(GObject *,GAsyncResult * res,void * user_data)1237 static void wxgtk_run_javascript_cb(GObject *,
1238 GAsyncResult *res,
1239 void *user_data)
1240 {
1241 g_object_ref(res);
1242
1243 GAsyncResult** res_out = static_cast<GAsyncResult**>(user_data);
1244 *res_out = res;
1245 }
1246
1247 } // extern "C"
1248
1249 // Run the given script synchronously and return its result in output.
RunScriptSync(const wxString & javascript,wxString * output) const1250 bool wxWebViewWebKit::RunScriptSync(const wxString& javascript, wxString* output) const
1251 {
1252 GAsyncResult *result = NULL;
1253 webkit_web_view_run_javascript(m_web_view,
1254 javascript.utf8_str(),
1255 NULL,
1256 wxgtk_run_javascript_cb,
1257 &result);
1258
1259 GMainContext *main_context = g_main_context_get_thread_default();
1260
1261 while ( !result )
1262 g_main_context_iteration(main_context, TRUE);
1263
1264 wxGtkError error;
1265 wxWebKitJavascriptResult js_result
1266 (
1267 webkit_web_view_run_javascript_finish
1268 (
1269 m_web_view,
1270 result,
1271 error.Out()
1272 )
1273 );
1274
1275 // Match g_object_ref() in wxgtk_run_javascript_cb()
1276 g_object_unref(result);
1277
1278 if ( !js_result )
1279 {
1280 if ( output )
1281 *output = error.GetMessage();
1282 return false;
1283 }
1284
1285 return wxGetStringFromJSResult(js_result, output);
1286 }
1287
RunScript(const wxString & javascript,wxString * output) const1288 bool wxWebViewWebKit::RunScript(const wxString& javascript, wxString* output) const
1289 {
1290 wxJSScriptWrapper wrapJS(javascript, &m_runScriptCount);
1291
1292 // This string is also used as an error indicator: it's cleared if there is
1293 // no error or used in the warning message below if there is one.
1294 wxString result;
1295 if ( RunScriptSync(wrapJS.GetWrappedCode(), &result)
1296 && result == wxS("true") )
1297 {
1298 if ( RunScriptSync(wrapJS.GetOutputCode(), &result) )
1299 {
1300 if ( output )
1301 *output = result;
1302 result.clear();
1303 }
1304
1305 RunScriptSync(wrapJS.GetCleanUpCode());
1306 }
1307
1308 if ( !result.empty() )
1309 {
1310 wxLogWarning(_("Error running JavaScript: %s"), result);
1311 return false;
1312 }
1313
1314 return true;
1315 }
1316
AddScriptMessageHandler(const wxString & name)1317 bool wxWebViewWebKit::AddScriptMessageHandler(const wxString& name)
1318 {
1319 if (!m_web_view)
1320 return false;
1321
1322 WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
1323 g_signal_connect(ucm, wxString::Format("script-message-received::%s", name).utf8_str(),
1324 G_CALLBACK(wxgtk_webview_webkit_script_message_received), this);
1325 bool res = webkit_user_content_manager_register_script_message_handler(ucm, name.utf8_str());
1326 if (res)
1327 {
1328 // Make webkit message handler available under common name
1329 wxString js = wxString::Format("window.%s = window.webkit.messageHandlers.%s;",
1330 name, name);
1331 AddUserScript(js);
1332 RunScript(js);
1333 }
1334
1335 return res;
1336 }
1337
RemoveScriptMessageHandler(const wxString & name)1338 bool wxWebViewWebKit::RemoveScriptMessageHandler(const wxString& name)
1339 {
1340 WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
1341 webkit_user_content_manager_unregister_script_message_handler(ucm, name.utf8_str());
1342 return true;
1343 }
1344
AddUserScript(const wxString & javascript,wxWebViewUserScriptInjectionTime injectionTime)1345 bool wxWebViewWebKit::AddUserScript(const wxString& javascript,
1346 wxWebViewUserScriptInjectionTime injectionTime)
1347 {
1348 WebKitUserScript* userScript = webkit_user_script_new(
1349 javascript.utf8_str(),
1350 WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
1351 (injectionTime == wxWEBVIEW_INJECT_AT_DOCUMENT_START) ?
1352 WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START : WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END,
1353 NULL, NULL
1354 );
1355 WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
1356 webkit_user_content_manager_add_script(ucm, userScript);
1357 webkit_user_script_unref(userScript);
1358
1359 return true;
1360 }
1361
RemoveAllUserScripts()1362 void wxWebViewWebKit::RemoveAllUserScripts()
1363 {
1364 WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
1365 webkit_user_content_manager_remove_all_scripts(ucm);
1366 }
1367
RegisterHandler(wxSharedPtr<wxWebViewHandler> handler)1368 void wxWebViewWebKit::RegisterHandler(wxSharedPtr<wxWebViewHandler> handler)
1369 {
1370 m_handlerList.push_back(handler);
1371 WebKitWebContext* context = webkit_web_context_get_default();
1372 webkit_web_context_register_uri_scheme(context, handler->GetName().utf8_str(),
1373 (WebKitURISchemeRequestCallback)wxgtk_webview_webkit_uri_scheme_request_cb,
1374 this, NULL);
1375 }
1376
EnableContextMenu(bool enable)1377 void wxWebViewWebKit::EnableContextMenu(bool enable)
1378 {
1379 wxWebView::EnableContextMenu(enable);
1380 }
1381
Find(const wxString & text,int flags)1382 long wxWebViewWebKit::Find(const wxString& text, int flags)
1383 {
1384 WebKitFindController* findctrl = webkit_web_view_get_find_controller(m_web_view);
1385 bool newSearch = false;
1386 if(text != m_findText ||
1387 (flags & wxWEBVIEW_FIND_MATCH_CASE) != (m_findFlags & wxWEBVIEW_FIND_MATCH_CASE))
1388 {
1389 newSearch = true;
1390 //If it is a new search we need to clear existing highlights
1391 webkit_find_controller_search_finish(findctrl);
1392 }
1393
1394 m_findFlags = flags;
1395 m_findText = text;
1396
1397 //If the search string is empty then we clear any selection and highlight
1398 if(text.empty())
1399 {
1400 webkit_find_controller_search_finish(findctrl);
1401 ClearSelection();
1402 return wxNOT_FOUND;
1403 }
1404
1405 bool wrap = false, forward = true;
1406 guint32 options = WEBKIT_FIND_OPTIONS_NONE;
1407 if(flags & wxWEBVIEW_FIND_WRAP)
1408 {
1409 wrap = true;
1410 options |= WEBKIT_FIND_OPTIONS_WRAP_AROUND;
1411 }
1412 if(!(flags & wxWEBVIEW_FIND_MATCH_CASE))
1413 {
1414 options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
1415 }
1416 if(flags & wxWEBVIEW_FIND_BACKWARDS)
1417 {
1418 forward = false;
1419 options |= WEBKIT_FIND_OPTIONS_BACKWARDS;
1420 }
1421
1422 if(newSearch)
1423 {
1424 //Initially we count the matches to know how many we have
1425 m_findCount = -1;
1426 webkit_find_controller_count_matches(findctrl,
1427 wxGTK_CONV(text),
1428 options,
1429 G_MAXUINT);
1430 GMainContext *main_context = g_main_context_get_thread_default();
1431 while (m_findCount == -1)
1432 {
1433 g_main_context_iteration(main_context, TRUE);
1434 }
1435 //Highlight them if needed
1436 if(flags & wxWEBVIEW_FIND_HIGHLIGHT_RESULT)
1437 {
1438 webkit_find_controller_search(findctrl,
1439 wxGTK_CONV(text),
1440 options,
1441 G_MAXUINT);
1442 }
1443 //In this case we return early to match IE behaviour
1444 m_findPosition = -1;
1445 return m_findCount;
1446 }
1447 else
1448 {
1449 if(forward)
1450 m_findPosition++;
1451 else
1452 m_findPosition--;
1453 if(m_findPosition < 0)
1454 m_findPosition += m_findCount;
1455 if(m_findPosition > m_findCount)
1456 m_findPosition -= m_findCount;
1457 }
1458
1459 if(forward)
1460 {
1461 webkit_find_controller_search_next(findctrl);
1462 if(m_findPosition == m_findCount && !wrap)
1463 {
1464 return wxNOT_FOUND;
1465 }
1466 }
1467 else
1468 {
1469 webkit_find_controller_search_previous(findctrl);
1470 if(m_findPosition == -1 && !wrap)
1471 {
1472 return wxNOT_FOUND;
1473 }
1474 }
1475
1476 return newSearch ? m_findCount : m_findPosition;
1477 }
1478
FindClear()1479 void wxWebViewWebKit::FindClear()
1480 {
1481 m_findCount = 0;
1482 m_findFlags = 0;
1483 m_findText.clear();
1484 m_findPosition = -1;
1485 }
1486
1487 // static
1488 wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant WXUNUSED (variant))1489 wxWebViewWebKit::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
1490 {
1491 return GetDefaultAttributesFromGTKWidget(webkit_web_view_new());
1492 }
1493
SetupWebExtensionServer()1494 void wxWebViewWebKit::SetupWebExtensionServer()
1495 {
1496 char *address = g_strdup_printf("unix:tmpdir=%s", g_get_tmp_dir());
1497 char *guid = g_dbus_generate_guid();
1498 GDBusAuthObserver *observer = g_dbus_auth_observer_new();
1499 GError *error = NULL;
1500
1501 g_signal_connect(observer, "authorize-authenticated-peer",
1502 G_CALLBACK(wxgtk_authorize_authenticated_peer_cb), this);
1503
1504 m_dbusServer = g_dbus_server_new_sync(address,
1505 G_DBUS_SERVER_FLAGS_NONE,
1506 guid,
1507 observer,
1508 NULL,
1509 &error);
1510
1511 if (error)
1512 {
1513 g_warning("Failed to start web extension server on %s: %s", address, error->message);
1514 g_error_free(error);
1515 }
1516 else
1517 {
1518 g_signal_connect(m_dbusServer, "new-connection",
1519 G_CALLBACK(wxgtk_new_connection_cb), &m_extension);
1520 g_dbus_server_start(m_dbusServer);
1521 }
1522
1523 g_free(address);
1524 g_free(guid);
1525 g_object_unref(observer);
1526 }
1527
GetExtensionProxy() const1528 GDBusProxy *wxWebViewWebKit::GetExtensionProxy() const
1529 {
1530 if (!m_extension)
1531 {
1532 g_warning("Web extension not found in \"%s\", "
1533 "some wxWebView functionality will be not available",
1534 (const char*)GetStandardWebExtensionsDir().utf8_str());
1535 }
1536 return m_extension;
1537 }
1538
1539 #endif // wxUSE_WEBVIEW && wxUSE_WEBVIEW_WEBKIT2
1540