2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
23 ==============================================================================
29 //==============================================================================
30 class WebKitSymbols
: public DeletedAtShutdown
33 //==============================================================================
34 bool isWebKitAvailable() const noexcept
{ return webKitIsAvailable
; }
36 //==============================================================================
37 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_settings_new
, juce_webkit_settings_new
,
40 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_settings_set_hardware_acceleration_policy
, juce_webkit_settings_set_hardware_acceleration_policy
,
41 (WebKitSettings
*, int), void)
43 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_web_view_new_with_settings
, juce_webkit_web_view_new_with_settings
,
44 (WebKitSettings
*), GtkWidget
*)
46 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_web_view_load_uri
, juce_webkit_web_view_load_uri
,
47 (WebKitWebView
*, const gchar
*), void)
49 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_policy_decision_use
, juce_webkit_policy_decision_use
,
50 (WebKitPolicyDecision
*), void)
52 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_policy_decision_ignore
, juce_webkit_policy_decision_ignore
,
53 (WebKitPolicyDecision
*), void)
55 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_web_view_go_back
, juce_webkit_web_view_go_back
,
56 (WebKitWebView
*), void)
58 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_web_view_go_forward
, juce_webkit_web_view_go_forward
,
59 (WebKitWebView
*), void)
61 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_web_view_reload
, juce_webkit_web_view_reload
,
62 (WebKitWebView
*), void)
64 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_web_view_stop_loading
, juce_webkit_web_view_stop_loading
,
65 (WebKitWebView
*), void)
67 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_uri_request_get_uri
, juce_webkit_uri_request_get_uri
,
68 (WebKitURIRequest
*), const gchar
*)
70 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_navigation_action_get_request
, juce_webkit_navigation_action_get_request
,
71 (WebKitNavigationAction
*), WebKitURIRequest
*)
73 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_navigation_policy_decision_get_frame_name
, juce_webkit_navigation_policy_decision_get_frame_name
,
74 (WebKitNavigationPolicyDecision
*), const gchar
*)
76 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_navigation_policy_decision_get_navigation_action
, juce_webkit_navigation_policy_decision_get_navigation_action
,
77 (WebKitNavigationPolicyDecision
*), WebKitNavigationAction
*)
79 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (webkit_web_view_get_uri
, juce_webkit_web_view_get_uri
,
80 (WebKitWebView
*), const gchar
*)
82 //==============================================================================
83 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_init
, juce_gtk_init
,
84 (int*, char***), void)
86 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_plug_new
, juce_gtk_plug_new
,
87 (::Window
), GtkWidget
*)
89 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_scrolled_window_new
, juce_gtk_scrolled_window_new
,
90 (GtkAdjustment
*, GtkAdjustment
*), GtkWidget
*)
92 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_container_add
, juce_gtk_container_add
,
93 (GtkContainer
*, GtkWidget
*), void)
95 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_widget_show_all
, juce_gtk_widget_show_all
,
98 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_plug_get_id
, juce_gtk_plug_get_id
,
101 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_main
, juce_gtk_main
,
104 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (gtk_main_quit
, juce_gtk_main_quit
,
107 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (g_unix_fd_add
, juce_g_unix_fd_add
,
108 (gint
, GIOCondition
, GUnixFDSourceFunc
, gpointer
), guint
)
110 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (g_object_ref
, juce_g_object_ref
,
111 (gpointer
), gpointer
)
113 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (g_object_unref
, juce_g_object_unref
,
116 JUCE_GENERATE_FUNCTION_WITH_DEFAULT (g_signal_connect_data
, juce_g_signal_connect_data
,
117 (gpointer
, const gchar
*, GCallback
, gpointer
, GClosureNotify
, GConnectFlags
), gulong
)
119 //==============================================================================
120 JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (WebKitSymbols
)
123 WebKitSymbols() = default;
127 clearSingletonInstance();
130 template <typename FuncPtr
>
137 template <typename FuncPtr
>
138 SymbolBinding
<FuncPtr
> makeSymbolBinding (FuncPtr
& func
, const char* name
)
140 return { func
, name
};
143 template <typename FuncPtr
>
144 bool loadSymbols (DynamicLibrary
& lib
, SymbolBinding
<FuncPtr
> binding
)
146 if (auto* func
= lib
.getFunction (binding
.name
))
148 binding
.func
= reinterpret_cast<FuncPtr
> (func
);
155 template <typename FuncPtr
, typename
... Args
>
156 bool loadSymbols (DynamicLibrary
& lib
, SymbolBinding
<FuncPtr
> binding
, Args
... args
)
158 return loadSymbols (lib
, binding
) && loadSymbols (lib
, args
...);
161 //==============================================================================
162 bool loadWebkitSymbols()
164 return loadSymbols (webkitLib
,
165 makeSymbolBinding (juce_webkit_settings_new
, "webkit_settings_new"),
166 makeSymbolBinding (juce_webkit_settings_set_hardware_acceleration_policy
, "webkit_settings_set_hardware_acceleration_policy"),
167 makeSymbolBinding (juce_webkit_web_view_new_with_settings
, "webkit_web_view_new_with_settings"),
168 makeSymbolBinding (juce_webkit_policy_decision_use
, "webkit_policy_decision_use"),
169 makeSymbolBinding (juce_webkit_policy_decision_ignore
, "webkit_policy_decision_ignore"),
170 makeSymbolBinding (juce_webkit_web_view_go_back
, "webkit_web_view_go_back"),
171 makeSymbolBinding (juce_webkit_web_view_go_forward
, "webkit_web_view_go_forward"),
172 makeSymbolBinding (juce_webkit_web_view_reload
, "webkit_web_view_reload"),
173 makeSymbolBinding (juce_webkit_web_view_stop_loading
, "webkit_web_view_stop_loading"),
174 makeSymbolBinding (juce_webkit_uri_request_get_uri
, "webkit_uri_request_get_uri"),
175 makeSymbolBinding (juce_webkit_web_view_load_uri
, "webkit_web_view_load_uri"),
176 makeSymbolBinding (juce_webkit_navigation_action_get_request
, "webkit_navigation_action_get_request"),
177 makeSymbolBinding (juce_webkit_navigation_policy_decision_get_frame_name
, "webkit_navigation_policy_decision_get_frame_name"),
178 makeSymbolBinding (juce_webkit_navigation_policy_decision_get_navigation_action
, "webkit_navigation_policy_decision_get_navigation_action"),
179 makeSymbolBinding (juce_webkit_web_view_get_uri
, "webkit_web_view_get_uri"));
182 bool loadGtkSymbols()
184 return loadSymbols (gtkLib
,
185 makeSymbolBinding (juce_gtk_init
, "gtk_init"),
186 makeSymbolBinding (juce_gtk_plug_new
, "gtk_plug_new"),
187 makeSymbolBinding (juce_gtk_scrolled_window_new
, "gtk_scrolled_window_new"),
188 makeSymbolBinding (juce_gtk_container_add
, "gtk_container_add"),
189 makeSymbolBinding (juce_gtk_widget_show_all
, "gtk_widget_show_all"),
190 makeSymbolBinding (juce_gtk_plug_get_id
, "gtk_plug_get_id"),
191 makeSymbolBinding (juce_gtk_main
, "gtk_main"),
192 makeSymbolBinding (juce_gtk_main_quit
, "gtk_main_quit"),
193 makeSymbolBinding (juce_g_unix_fd_add
, "g_unix_fd_add"),
194 makeSymbolBinding (juce_g_object_ref
, "g_object_ref"),
195 makeSymbolBinding (juce_g_object_unref
, "g_object_unref"),
196 makeSymbolBinding (juce_g_signal_connect_data
, "g_signal_connect_data"));
199 //==============================================================================
200 DynamicLibrary gtkLib
{ "libgtk-3.so" }, webkitLib
{ "libwebkit2gtk-4.0.so" };
201 const bool webKitIsAvailable
= loadWebkitSymbols() && loadGtkSymbols();
203 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebKitSymbols
)
206 JUCE_IMPLEMENT_SINGLETON (WebKitSymbols
)
208 //==============================================================================
209 extern int juce_gtkWebkitMain (int argc
, const char* argv
[]);
211 class CommandReceiver
216 virtual ~Responder() {}
218 virtual void handleCommand (const String
& cmd
, const var
& param
) = 0;
219 virtual void receiverHadError() = 0;
222 CommandReceiver (Responder
* responderToUse
, int inputChannelToUse
)
223 : responder (responderToUse
), inChannel (inputChannelToUse
)
225 setBlocking (inChannel
, false);
228 static void setBlocking (int fd
, bool shouldBlock
)
230 auto flags
= fcntl (fd
, F_GETFL
);
231 fcntl (fd
, F_SETFL
, (shouldBlock
? (flags
& ~O_NONBLOCK
)
232 : (flags
| O_NONBLOCK
)));
235 int getFd() const { return inChannel
; }
241 auto len
= (receivingLength
? sizeof (size_t) : bufferLength
.len
);
243 if (! receivingLength
)
244 buffer
.realloc (len
);
246 auto* dst
= (receivingLength
? bufferLength
.data
: buffer
.getData());
248 auto actual
= read (inChannel
, &dst
[pos
], static_cast<size_t> (len
- pos
));
258 pos
+= static_cast<size_t> (actual
);
264 if (! receivingLength
)
265 parseJSON (String (buffer
.getData(), bufferLength
.len
));
267 receivingLength
= (! receivingLength
);
271 if (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&& responder
!= nullptr)
272 responder
->receiverHadError();
275 static void sendCommand (int outChannel
, const String
& cmd
, const var
& params
)
277 DynamicObject::Ptr obj
= new DynamicObject
;
279 obj
->setProperty (getCmdIdentifier(), cmd
);
281 if (! params
.isVoid())
282 obj
->setProperty (getParamIdentifier(), params
);
284 auto json
= JSON::toString (var (obj
.get()));
286 auto jsonLength
= static_cast<size_t> (json
.length());
287 auto len
= sizeof (size_t) + jsonLength
;
289 HeapBlock
<char> buffer (len
);
290 auto* dst
= buffer
.getData();
292 memcpy (dst
, &jsonLength
, sizeof (size_t));
293 dst
+= sizeof (size_t);
295 memcpy (dst
, json
.toRawUTF8(), jsonLength
);
301 ret
= write (outChannel
, buffer
.getData(), len
);
303 if (ret
!= -1 || errno
!= EINTR
)
309 void parseJSON (const String
& json
)
311 auto object
= JSON::fromString (json
);
313 if (! object
.isVoid())
315 auto cmd
= object
.getProperty (getCmdIdentifier(), {}).toString();
316 auto params
= object
.getProperty (getParamIdentifier(), {});
318 if (responder
!= nullptr)
319 responder
->handleCommand (cmd
, params
);
323 static Identifier
getCmdIdentifier() { static Identifier
Id ("cmd"); return Id
; }
324 static Identifier
getParamIdentifier() { static Identifier
Id ("params"); return Id
; }
326 Responder
* responder
= nullptr;
329 bool receivingLength
= true;
330 union { char data
[sizeof (size_t)]; size_t len
; } bufferLength
;
331 HeapBlock
<char> buffer
;
334 #define juce_g_signal_connect(instance, detailed_signal, c_handler, data) \
335 WebKitSymbols::getInstance()->juce_g_signal_connect_data (instance, detailed_signal, c_handler, data, nullptr, (GConnectFlags) 0)
337 //==============================================================================
338 class GtkChildProcess
: private CommandReceiver::Responder
341 //==============================================================================
342 GtkChildProcess (int inChannel
, int outChannelToUse
)
343 : outChannel (outChannelToUse
),
344 receiver (this, inChannel
)
349 CommandReceiver::setBlocking (outChannel
, true);
351 WebKitSymbols::getInstance()->juce_gtk_init (nullptr, nullptr);
353 auto* settings
= WebKitSymbols::getInstance()->juce_webkit_settings_new();
354 WebKitSymbols::getInstance()->juce_webkit_settings_set_hardware_acceleration_policy (settings
,
355 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */ 2);
357 auto* plug
= WebKitSymbols::getInstance()->juce_gtk_plug_new (0);
358 auto* container
= WebKitSymbols::getInstance()->juce_gtk_scrolled_window_new (nullptr, nullptr);
360 auto* webviewWidget
= WebKitSymbols::getInstance()->juce_webkit_web_view_new_with_settings (settings
);
361 webview
= (WebKitWebView
*) webviewWidget
;
363 WebKitSymbols::getInstance()->juce_gtk_container_add ((GtkContainer
*) container
, webviewWidget
);
364 WebKitSymbols::getInstance()->juce_gtk_container_add ((GtkContainer
*) plug
, container
);
366 WebKitSymbols::getInstance()->juce_webkit_web_view_load_uri (webview
, "about:blank");
368 juce_g_signal_connect (webview
, "decide-policy",
369 (GCallback
) decidePolicyCallback
, this);
371 juce_g_signal_connect (webview
, "load-changed",
372 (GCallback
) loadChangedCallback
, this);
374 juce_g_signal_connect (webview
, "load-failed",
375 (GCallback
) loadFailedCallback
, this);
377 WebKitSymbols::getInstance()->juce_gtk_widget_show_all (plug
);
378 auto wID
= (unsigned long) WebKitSymbols::getInstance()->juce_gtk_plug_get_id ((GtkPlug
*) plug
);
384 ret
= write (outChannel
, &wID
, sizeof (wID
));
386 if (ret
!= -1 || errno
!= EINTR
)
390 WebKitSymbols::getInstance()->juce_g_unix_fd_add (receiver
.getFd(), G_IO_IN
, pipeReadyStatic
, this);
391 receiver
.tryNextRead();
393 WebKitSymbols::getInstance()->juce_gtk_main();
395 WebKitSymbols::getInstance()->deleteInstance();
399 void goToURL (const var
& params
)
401 static Identifier
urlIdentifier ("url");
402 auto url
= params
.getProperty (urlIdentifier
, var()).toString();
404 WebKitSymbols::getInstance()->juce_webkit_web_view_load_uri (webview
, url
.toRawUTF8());
407 void handleDecisionResponse (const var
& params
)
409 auto* decision
= (WebKitPolicyDecision
*) ((int64
) params
.getProperty ("decision_id", var (0)));
410 bool allow
= params
.getProperty ("allow", var (false));
412 if (decision
!= nullptr && decisions
.contains (decision
))
415 WebKitSymbols::getInstance()->juce_webkit_policy_decision_use (decision
);
417 WebKitSymbols::getInstance()->juce_webkit_policy_decision_ignore (decision
);
419 decisions
.removeAllInstancesOf (decision
);
420 WebKitSymbols::getInstance()->juce_g_object_unref (decision
);
424 //==============================================================================
425 void handleCommand (const String
& cmd
, const var
& params
) override
427 if (cmd
== "quit") quit();
428 else if (cmd
== "goToURL") goToURL (params
);
429 else if (cmd
== "goBack") WebKitSymbols::getInstance()->juce_webkit_web_view_go_back (webview
);
430 else if (cmd
== "goForward") WebKitSymbols::getInstance()->juce_webkit_web_view_go_forward (webview
);
431 else if (cmd
== "refresh") WebKitSymbols::getInstance()->juce_webkit_web_view_reload (webview
);
432 else if (cmd
== "stop") WebKitSymbols::getInstance()->juce_webkit_web_view_stop_loading (webview
);
433 else if (cmd
== "decision") handleDecisionResponse (params
);
436 void receiverHadError() override
441 //==============================================================================
442 bool pipeReady (gint fd
, GIOCondition
)
444 if (fd
== receiver
.getFd())
446 receiver
.tryNextRead();
455 WebKitSymbols::getInstance()->juce_gtk_main_quit();
458 String
getURIStringForAction (WebKitNavigationAction
* action
)
460 auto* request
= WebKitSymbols::getInstance()->juce_webkit_navigation_action_get_request (action
);
461 return WebKitSymbols::getInstance()->juce_webkit_uri_request_get_uri (request
);
464 bool onNavigation (String frameName
,
465 WebKitNavigationAction
* action
,
466 WebKitPolicyDecision
* decision
)
468 if (decision
!= nullptr && frameName
.isEmpty())
470 WebKitSymbols::getInstance()->juce_g_object_ref (decision
);
471 decisions
.add (decision
);
473 DynamicObject::Ptr params
= new DynamicObject
;
475 params
->setProperty ("url", getURIStringForAction (action
));
476 params
->setProperty ("decision_id", (int64
) decision
);
477 CommandReceiver::sendCommand (outChannel
, "pageAboutToLoad", var (params
.get()));
485 bool onNewWindow (String
/*frameName*/,
486 WebKitNavigationAction
* action
,
487 WebKitPolicyDecision
* decision
)
489 if (decision
!= nullptr)
491 DynamicObject::Ptr params
= new DynamicObject
;
493 params
->setProperty ("url", getURIStringForAction (action
));
494 CommandReceiver::sendCommand (outChannel
, "newWindowAttemptingToLoad", var (params
.get()));
496 // never allow new windows
497 WebKitSymbols::getInstance()->juce_webkit_policy_decision_ignore (decision
);
505 void onLoadChanged (WebKitLoadEvent loadEvent
)
507 if (loadEvent
== WEBKIT_LOAD_FINISHED
)
509 DynamicObject::Ptr params
= new DynamicObject
;
511 params
->setProperty ("url", String (WebKitSymbols::getInstance()->juce_webkit_web_view_get_uri (webview
)));
512 CommandReceiver::sendCommand (outChannel
, "pageFinishedLoading", var (params
.get()));
516 bool onDecidePolicy (WebKitPolicyDecision
* decision
,
517 WebKitPolicyDecisionType decisionType
)
519 switch (decisionType
)
521 case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION
:
523 auto* navigationDecision
= (WebKitNavigationPolicyDecision
*) decision
;
524 auto* frameName
= WebKitSymbols::getInstance()->juce_webkit_navigation_policy_decision_get_frame_name (navigationDecision
);
526 return onNavigation (String (frameName
!= nullptr ? frameName
: ""),
527 WebKitSymbols::getInstance()->juce_webkit_navigation_policy_decision_get_navigation_action (navigationDecision
),
531 case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION
:
533 auto* navigationDecision
= (WebKitNavigationPolicyDecision
*) decision
;
534 auto* frameName
= WebKitSymbols::getInstance()->juce_webkit_navigation_policy_decision_get_frame_name (navigationDecision
);
536 return onNewWindow (String (frameName
!= nullptr ? frameName
: ""),
537 WebKitSymbols::getInstance()->juce_webkit_navigation_policy_decision_get_navigation_action (navigationDecision
),
541 case WEBKIT_POLICY_DECISION_TYPE_RESPONSE
:
543 auto* response
= (WebKitNavigationPolicyDecision
*) decision
;
545 // for now just always allow response requests
546 ignoreUnused (response
);
547 WebKitSymbols::getInstance()->juce_webkit_policy_decision_use (decision
);
558 void onLoadFailed (GError
* error
)
560 DynamicObject::Ptr params
= new DynamicObject
;
562 params
->setProperty ("error", String (error
!= nullptr ? error
->message
: "unknown error"));
563 CommandReceiver::sendCommand (outChannel
, "pageLoadHadNetworkError", var (params
.get()));
567 static gboolean
pipeReadyStatic (gint fd
, GIOCondition condition
, gpointer user
)
569 return (reinterpret_cast<GtkChildProcess
*> (user
)->pipeReady (fd
, condition
) ? TRUE
: FALSE
);
572 static gboolean
decidePolicyCallback (WebKitWebView
*,
573 WebKitPolicyDecision
* decision
,
574 WebKitPolicyDecisionType decisionType
,
577 auto& owner
= *reinterpret_cast<GtkChildProcess
*> (user
);
578 return (owner
.onDecidePolicy (decision
, decisionType
) ? TRUE
: FALSE
);
581 static void loadChangedCallback (WebKitWebView
*,
582 WebKitLoadEvent loadEvent
,
585 auto& owner
= *reinterpret_cast<GtkChildProcess
*> (user
);
586 owner
.onLoadChanged (loadEvent
);
589 static void loadFailedCallback (WebKitWebView
*,
590 WebKitLoadEvent
/*loadEvent*/,
591 gchar
* /*failing_uri*/,
595 auto& owner
= *reinterpret_cast<GtkChildProcess
*> (user
);
596 owner
.onLoadFailed (error
);
600 CommandReceiver receiver
;
601 WebKitWebView
* webview
= nullptr;
602 Array
<WebKitPolicyDecision
*> decisions
;
605 //==============================================================================
606 class WebBrowserComponent::Pimpl
: private Thread
,
607 private CommandReceiver::Responder
610 Pimpl (WebBrowserComponent
& parent
)
611 : Thread ("Webview"), owner (parent
)
613 webKitIsAvailable
= WebKitSymbols::getInstance()->isWebKitAvailable();
621 //==============================================================================
624 if (! webKitIsAvailable
)
629 auto ret
= pipe (threadControl
);
634 CommandReceiver::setBlocking (inChannel
, true);
635 CommandReceiver::setBlocking (outChannel
, true);
636 CommandReceiver::setBlocking (threadControl
[0], false);
637 CommandReceiver::setBlocking (threadControl
[1], true);
639 unsigned long windowHandle
;
640 auto actual
= read (inChannel
, &windowHandle
, sizeof (windowHandle
));
642 if (actual
!= (ssize_t
) sizeof (windowHandle
))
648 receiver
.reset (new CommandReceiver (this, inChannel
));
650 pfds
.push_back ({ threadControl
[0], POLLIN
, 0 });
651 pfds
.push_back ({ receiver
->getFd(), POLLIN
, 0 });
655 xembed
.reset (new XEmbedComponent (windowHandle
));
656 owner
.addAndMakeVisible (xembed
.get());
661 if (! webKitIsAvailable
)
664 if (isThreadRunning())
666 signalThreadShouldExit();
673 ret
= write (threadControl
[1], &ignore
, 1);
675 if (ret
!= -1 || errno
!= EINTR
)
679 waitForThreadToExit (-1);
683 if (childProcess
!= 0)
685 CommandReceiver::sendCommand (outChannel
, "quit", {});
690 //==============================================================================
691 void goToURL (const String
& url
, const StringArray
* headers
, const MemoryBlock
* postData
)
693 if (! webKitIsAvailable
)
696 DynamicObject::Ptr params
= new DynamicObject
;
698 params
->setProperty ("url", url
);
700 if (headers
!= nullptr)
701 params
->setProperty ("headers", var (*headers
));
703 if (postData
!= nullptr)
704 params
->setProperty ("postData", var (*postData
));
706 CommandReceiver::sendCommand (outChannel
, "goToURL", var (params
.get()));
709 void goBack() { if (webKitIsAvailable
) CommandReceiver::sendCommand (outChannel
, "goBack", {}); }
710 void goForward() { if (webKitIsAvailable
) CommandReceiver::sendCommand (outChannel
, "goForward", {}); }
711 void refresh() { if (webKitIsAvailable
) CommandReceiver::sendCommand (outChannel
, "refresh", {}); }
712 void stop() { if (webKitIsAvailable
) CommandReceiver::sendCommand (outChannel
, "stop", {}); }
716 if (xembed
!= nullptr)
717 xembed
->setBounds (owner
.getLocalBounds());
721 //==============================================================================
724 if (childProcess
!= 0)
728 int status
= 0, result
= 0;
730 result
= waitpid (childProcess
, &status
, WNOHANG
);
731 for (int i
= 0; i
< 15 && (! WIFEXITED(status
) || result
!= childProcess
); ++i
)
734 result
= waitpid (childProcess
, &status
, WNOHANG
);
737 // clean-up any zombies
739 if (! WIFEXITED(status
) || result
!= childProcess
)
743 kill (childProcess
, SIGTERM
);
744 waitpid (childProcess
, &status
, 0);
746 if (WIFEXITED (status
))
757 int inPipe
[2], outPipe
[2];
759 auto ret
= pipe (inPipe
);
760 ignoreUnused (ret
); jassert (ret
== 0);
762 ret
= pipe (outPipe
);
763 ignoreUnused (ret
); jassert (ret
== 0);
771 HeapBlock
<const char*> argv (5);
772 StringArray arguments
;
774 arguments
.add (File::getSpecialLocation (File::currentExecutableFile
).getFullPathName());
775 arguments
.add ("--juce-gtkwebkitfork-child");
776 arguments
.add (String (outPipe
[0]));
777 arguments
.add (String (inPipe
[1]));
779 for (int i
= 0; i
< arguments
.size(); ++i
)
780 argv
[i
] = arguments
[i
].toRawUTF8();
784 #if JUCE_STANDALONE_APPLICATION
785 execv (arguments
[0].toRawUTF8(), (char**) argv
.getData());
787 juce_gtkWebkitMain (4, (const char**) argv
.getData());
795 inChannel
= inPipe
[0];
796 outChannel
= outPipe
[1];
803 while (! threadShouldExit())
808 receiver
->tryNextRead();
812 while (result
== 0 || (result
< 0 && errno
== EINTR
))
813 result
= poll (&pfds
.front(), static_cast<nfds_t
> (pfds
.size()), 0);
823 auto result
= read (threadControl
[0], &ignore
, 1);
825 return (result
!= -1 || (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
));
828 //==============================================================================
829 void handleCommandOnMessageThread (const String
& cmd
, const var
& params
)
831 auto url
= params
.getProperty ("url", var()).toString();
833 if (cmd
== "pageAboutToLoad") handlePageAboutToLoad (url
, params
);
834 else if (cmd
== "pageFinishedLoading") owner
.pageFinishedLoading (url
);
835 else if (cmd
== "windowCloseRequest") owner
.windowCloseRequest();
836 else if (cmd
== "newWindowAttemptingToLoad") owner
.newWindowAttemptingToLoad (url
);
837 else if (cmd
== "pageLoadHadNetworkError") handlePageLoadHadNetworkError (params
);
839 threadBlocker
.signal();
842 void handlePageAboutToLoad (const String
& url
, const var
& inputParams
)
844 int64 decision_id
= inputParams
.getProperty ("decision_id", var (0));
846 if (decision_id
!= 0)
848 DynamicObject::Ptr params
= new DynamicObject
;
850 params
->setProperty ("decision_id", decision_id
);
851 params
->setProperty ("allow", owner
.pageAboutToLoad (url
));
853 CommandReceiver::sendCommand (outChannel
, "decision", var (params
.get()));
857 void handlePageLoadHadNetworkError (const var
& params
)
859 String error
= params
.getProperty ("error", "Unknown error");
861 if (owner
.pageLoadHadNetworkError (error
))
862 goToURL (String ("data:text/plain,") + error
, nullptr, nullptr);
865 void handleCommand (const String
& cmd
, const var
& params
) override
867 threadBlocker
.reset();
869 (new HandleOnMessageThread (this, cmd
, params
))->post();
871 // wait until the command has executed on the message thread
872 // this ensures that Pimpl can never be deleted while the
873 // message has not been executed yet
874 threadBlocker
.wait (-1);
877 void receiverHadError() override
{}
879 //==============================================================================
880 struct HandleOnMessageThread
: public CallbackMessage
882 HandleOnMessageThread (Pimpl
* pimpl
, const String
& cmdToUse
, const var
& params
)
883 : owner (pimpl
), cmdToSend (cmdToUse
), paramsToSend (params
)
886 void messageCallback() override
888 owner
->handleCommandOnMessageThread (cmdToSend
, paramsToSend
);
891 Pimpl
* owner
= nullptr;
896 bool webKitIsAvailable
= false;
898 WebBrowserComponent
& owner
;
899 std::unique_ptr
<CommandReceiver
> receiver
;
900 int childProcess
= 0, inChannel
= 0, outChannel
= 0;
901 int threadControl
[2];
902 std::unique_ptr
<XEmbedComponent
> xembed
;
903 WaitableEvent threadBlocker
;
904 std::vector
<pollfd
> pfds
;
907 //==============================================================================
908 WebBrowserComponent::WebBrowserComponent (const bool unloadWhenHidden
)
909 : browser (new Pimpl (*this)),
910 unloadPageWhenHidden (unloadWhenHidden
)
912 ignoreUnused (blankPageShown
);
913 ignoreUnused (unloadPageWhenHidden
);
920 WebBrowserComponent::~WebBrowserComponent()
924 //==============================================================================
925 void WebBrowserComponent::goToURL (const String
& url
,
926 const StringArray
* headers
,
927 const MemoryBlock
* postData
)
931 if (headers
!= nullptr)
932 lastHeaders
= *headers
;
936 if (postData
!= nullptr)
937 lastPostData
= *postData
;
939 lastPostData
.reset();
941 browser
->goToURL (url
, headers
, postData
);
944 void WebBrowserComponent::stop()
949 void WebBrowserComponent::goBack()
956 void WebBrowserComponent::goForward()
959 browser
->goForward();
962 void WebBrowserComponent::refresh()
967 //==============================================================================
968 void WebBrowserComponent::paint (Graphics
& g
)
970 g
.fillAll (Colours::white
);
973 void WebBrowserComponent::checkWindowAssociation()
977 void WebBrowserComponent::reloadLastURL()
979 if (lastURL
.isNotEmpty())
981 goToURL (lastURL
, &lastHeaders
, &lastPostData
);
986 void WebBrowserComponent::parentHierarchyChanged()
988 checkWindowAssociation();
991 void WebBrowserComponent::resized()
993 if (browser
!= nullptr)
997 void WebBrowserComponent::visibilityChanged()
999 checkWindowAssociation();
1002 void WebBrowserComponent::focusGained (FocusChangeType
)
1006 void WebBrowserComponent::clearCookies()
1008 // Currently not implemented on linux as WebBrowserComponent currently does not
1009 // store cookies on linux
1013 int juce_gtkWebkitMain (int argc
, const char* argv
[])
1018 GtkChildProcess
child (String (argv
[2]).getIntValue(),
1019 String (argv
[3]).getIntValue());
1021 return child
.entry();