fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).
This commit is contained in:
@ -0,0 +1,824 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
extern int juce_gtkWebkitMain (int argc, const char* argv[]);
|
||||
|
||||
class CommandReceiver
|
||||
{
|
||||
public:
|
||||
struct Responder
|
||||
{
|
||||
virtual ~Responder() {}
|
||||
|
||||
virtual void handleCommand (const String& cmd, const var& param) = 0;
|
||||
virtual void receiverHadError() = 0;
|
||||
};
|
||||
|
||||
CommandReceiver (Responder* responderToUse, int inputChannelToUse)
|
||||
: responder (responderToUse), inChannel (inputChannelToUse)
|
||||
{
|
||||
setBlocking (inChannel, false);
|
||||
}
|
||||
|
||||
static void setBlocking (int fd, bool shouldBlock)
|
||||
{
|
||||
int flags = fcntl (fd, F_GETFL);
|
||||
fcntl (fd, F_SETFL, (shouldBlock ? (flags & ~O_NONBLOCK)
|
||||
: (flags | O_NONBLOCK)));
|
||||
}
|
||||
|
||||
int getFd() const { return inChannel; }
|
||||
|
||||
void tryNextRead()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
size_t len = (receivingLength ? sizeof (size_t) : bufferLength.len);
|
||||
|
||||
if (! receivingLength)
|
||||
buffer.realloc (len);
|
||||
|
||||
char* dst = (receivingLength ? bufferLength.data : buffer.getData());
|
||||
|
||||
ssize_t actual = read (inChannel, &dst[pos], static_cast<size_t> (len - pos));
|
||||
|
||||
if (actual < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
pos += static_cast<size_t> (actual);
|
||||
|
||||
if (pos == len)
|
||||
{
|
||||
pos = 0;
|
||||
|
||||
if (! receivingLength)
|
||||
parseJSON (String (buffer.getData(), bufferLength.len));
|
||||
|
||||
receivingLength = (! receivingLength);
|
||||
}
|
||||
}
|
||||
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK && responder != nullptr)
|
||||
responder->receiverHadError();
|
||||
}
|
||||
|
||||
static void sendCommand (int outChannel, const String& cmd, const var& params)
|
||||
{
|
||||
DynamicObject::Ptr obj = new DynamicObject;
|
||||
|
||||
obj->setProperty (getCmdIdentifier(), cmd);
|
||||
|
||||
if (! params.isVoid())
|
||||
obj->setProperty (getParamIdentifier(), params);
|
||||
|
||||
String json (JSON::toString (var (obj)));
|
||||
|
||||
size_t jsonLength = static_cast<size_t> (json.length());
|
||||
size_t len = sizeof (size_t) + jsonLength;
|
||||
|
||||
HeapBlock<char> buffer (len);
|
||||
char* dst = buffer.getData();
|
||||
|
||||
memcpy (dst, &jsonLength, sizeof (size_t));
|
||||
dst += sizeof (size_t);
|
||||
|
||||
memcpy (dst, json.toRawUTF8(), jsonLength);
|
||||
|
||||
ssize_t ret;
|
||||
|
||||
do
|
||||
{
|
||||
ret = write (outChannel, buffer.getData(), len);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
}
|
||||
|
||||
private:
|
||||
void parseJSON (const String& json)
|
||||
{
|
||||
var object (JSON::fromString (json));
|
||||
|
||||
if (! object.isVoid())
|
||||
{
|
||||
String cmd (object.getProperty (getCmdIdentifier(), var()).toString());
|
||||
var params (object.getProperty (getParamIdentifier(), var()));
|
||||
|
||||
if (responder != nullptr)
|
||||
responder->handleCommand (cmd, params);
|
||||
}
|
||||
}
|
||||
|
||||
static Identifier getCmdIdentifier() { static Identifier Id ("cmd"); return Id; }
|
||||
static Identifier getParamIdentifier() { static Identifier Id ("params"); return Id; }
|
||||
|
||||
Responder* responder;
|
||||
int inChannel;
|
||||
size_t pos = 0;
|
||||
bool receivingLength = true;
|
||||
union { char data [sizeof (size_t)]; size_t len; } bufferLength;
|
||||
HeapBlock<char> buffer;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class GtkChildProcess : private CommandReceiver::Responder
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
GtkChildProcess (int inChannel, int outChannelToUse)
|
||||
: outChannel (outChannelToUse), receiver (this, inChannel)
|
||||
{}
|
||||
|
||||
typedef void (*SetHardwareAcclPolicyFunctionPtr) (WebKitSettings*, int);
|
||||
|
||||
int entry()
|
||||
{
|
||||
CommandReceiver::setBlocking (outChannel, true);
|
||||
|
||||
gtk_init (nullptr, nullptr);
|
||||
|
||||
WebKitSettings* settings = webkit_settings_new();
|
||||
|
||||
// webkit_settings_set_hardware_acceleration_policy was only added recently to webkit2
|
||||
// but is needed when running a WebBrowserComponent in a Parallels VM with 3D acceleration enabled
|
||||
auto setHardwarePolicy
|
||||
= reinterpret_cast<SetHardwareAcclPolicyFunctionPtr> (dlsym (RTLD_DEFAULT, "webkit_settings_set_hardware_acceleration_policy"));
|
||||
|
||||
if (setHardwarePolicy != nullptr)
|
||||
setHardwarePolicy (settings, 2 /*WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER*/);
|
||||
|
||||
GtkWidget *plug;
|
||||
|
||||
plug = gtk_plug_new(0);
|
||||
GtkWidget* container;
|
||||
container = gtk_scrolled_window_new (nullptr, nullptr);
|
||||
|
||||
GtkWidget* webviewWidget = webkit_web_view_new_with_settings (settings);
|
||||
webview = WEBKIT_WEB_VIEW (webviewWidget);
|
||||
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (container), webviewWidget);
|
||||
gtk_container_add (GTK_CONTAINER (plug), container);
|
||||
|
||||
webkit_web_view_load_uri (webview, "about:blank");
|
||||
|
||||
g_signal_connect (webview, "decide-policy",
|
||||
G_CALLBACK (decidePolicyCallback), this);
|
||||
|
||||
g_signal_connect (webview, "load-changed",
|
||||
G_CALLBACK (loadChangedCallback), this);
|
||||
|
||||
g_signal_connect (webview, "load-failed",
|
||||
G_CALLBACK (loadFailedCallback), this);
|
||||
|
||||
gtk_widget_show_all (plug);
|
||||
unsigned long wID = (unsigned long) gtk_plug_get_id (GTK_PLUG (plug));
|
||||
|
||||
|
||||
ssize_t ret;
|
||||
|
||||
do {
|
||||
ret = write (outChannel, &wID, sizeof (wID));
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
|
||||
g_unix_fd_add (receiver.getFd(), G_IO_IN, pipeReadyStatic, this);
|
||||
receiver.tryNextRead();
|
||||
|
||||
gtk_main();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void goToURL (const var& params)
|
||||
{
|
||||
static Identifier urlIdentifier ("url");
|
||||
String url (params.getProperty (urlIdentifier, var()).toString());
|
||||
|
||||
webkit_web_view_load_uri (webview, url.toRawUTF8());
|
||||
}
|
||||
|
||||
void handleDecisionResponse (const var& params)
|
||||
{
|
||||
WebKitPolicyDecision* decision
|
||||
= (WebKitPolicyDecision*) ((int64) params.getProperty ("decision_id", var (0)));
|
||||
bool allow = params.getProperty ("allow", var (false));
|
||||
|
||||
if (decision != nullptr && decisions.contains (decision))
|
||||
{
|
||||
if (allow)
|
||||
webkit_policy_decision_use (decision);
|
||||
else
|
||||
webkit_policy_decision_ignore (decision);
|
||||
|
||||
decisions.removeAllInstancesOf (decision);
|
||||
g_object_unref (decision);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleCommand (const String& cmd, const var& params) override
|
||||
{
|
||||
if (cmd == "quit") quit();
|
||||
else if (cmd == "goToURL") goToURL (params);
|
||||
else if (cmd == "goBack") webkit_web_view_go_back (webview);
|
||||
else if (cmd == "goForward") webkit_web_view_go_forward (webview);
|
||||
else if (cmd == "refresh") webkit_web_view_reload (webview);
|
||||
else if (cmd == "stop") webkit_web_view_stop_loading (webview);
|
||||
else if (cmd == "decision") handleDecisionResponse (params);
|
||||
}
|
||||
|
||||
void receiverHadError() override
|
||||
{
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool pipeReady (gint fd, GIOCondition)
|
||||
{
|
||||
if (fd == receiver.getFd())
|
||||
{
|
||||
receiver.tryNextRead();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
bool onNavigation (String frameName,
|
||||
WebKitNavigationAction* action,
|
||||
WebKitPolicyDecision* decision)
|
||||
{
|
||||
if (decision != nullptr && frameName.isEmpty())
|
||||
{
|
||||
g_object_ref (decision);
|
||||
decisions.add (decision);
|
||||
|
||||
DynamicObject::Ptr params = new DynamicObject;
|
||||
|
||||
params->setProperty ("url", String (webkit_uri_request_get_uri (webkit_navigation_action_get_request (action))));
|
||||
params->setProperty ("decision_id", (int64) decision);
|
||||
CommandReceiver::sendCommand (outChannel, "pageAboutToLoad", var (params));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool onNewWindow (String /*frameName*/,
|
||||
WebKitNavigationAction* action,
|
||||
WebKitPolicyDecision* decision)
|
||||
{
|
||||
if (decision != nullptr)
|
||||
{
|
||||
DynamicObject::Ptr params = new DynamicObject;
|
||||
|
||||
params->setProperty ("url", String (webkit_uri_request_get_uri (webkit_navigation_action_get_request (action))));
|
||||
CommandReceiver::sendCommand (outChannel, "newWindowAttemptingToLoad", var (params));
|
||||
|
||||
// never allow new windows
|
||||
webkit_policy_decision_ignore (decision);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void onLoadChanged (WebKitLoadEvent loadEvent)
|
||||
{
|
||||
if (loadEvent == WEBKIT_LOAD_FINISHED)
|
||||
{
|
||||
DynamicObject::Ptr params = new DynamicObject;
|
||||
|
||||
params->setProperty ("url", String (webkit_web_view_get_uri (webview)));
|
||||
CommandReceiver::sendCommand (outChannel, "pageFinishedLoading", var (params));
|
||||
}
|
||||
}
|
||||
|
||||
bool onDecidePolicy (WebKitPolicyDecision* decision,
|
||||
WebKitPolicyDecisionType decisionType)
|
||||
{
|
||||
switch (decisionType)
|
||||
{
|
||||
case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
|
||||
{
|
||||
WebKitNavigationPolicyDecision* navigationDecision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
|
||||
const char* frameName = webkit_navigation_policy_decision_get_frame_name (navigationDecision);
|
||||
|
||||
return onNavigation (String (frameName != nullptr ? frameName : ""),
|
||||
webkit_navigation_policy_decision_get_navigation_action (navigationDecision),
|
||||
decision);
|
||||
}
|
||||
break;
|
||||
case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
|
||||
{
|
||||
WebKitNavigationPolicyDecision* navigationDecision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
|
||||
const char* frameName = webkit_navigation_policy_decision_get_frame_name (navigationDecision);
|
||||
|
||||
return onNewWindow (String (frameName != nullptr ? frameName : ""),
|
||||
webkit_navigation_policy_decision_get_navigation_action (navigationDecision),
|
||||
decision);
|
||||
}
|
||||
break;
|
||||
case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
|
||||
{
|
||||
WebKitResponsePolicyDecision *response = WEBKIT_RESPONSE_POLICY_DECISION (decision);
|
||||
|
||||
// for now just always allow response requests
|
||||
ignoreUnused (response);
|
||||
webkit_policy_decision_use (decision);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void onLoadFailed (GError* error)
|
||||
{
|
||||
DynamicObject::Ptr params = new DynamicObject;
|
||||
|
||||
params->setProperty ("error", String (error != nullptr ? error->message : "unknown error"));
|
||||
CommandReceiver::sendCommand (outChannel, "pageLoadHadNetworkError", var (params));
|
||||
}
|
||||
|
||||
private:
|
||||
static gboolean pipeReadyStatic (gint fd, GIOCondition condition, gpointer user)
|
||||
{
|
||||
return (reinterpret_cast<GtkChildProcess*> (user)->pipeReady (fd, condition) ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static gboolean decidePolicyCallback (WebKitWebView*,
|
||||
WebKitPolicyDecision* decision,
|
||||
WebKitPolicyDecisionType decisionType,
|
||||
gpointer user)
|
||||
{
|
||||
GtkChildProcess& owner = *reinterpret_cast<GtkChildProcess*> (user);
|
||||
return (owner.onDecidePolicy (decision, decisionType) ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static void loadChangedCallback (WebKitWebView*,
|
||||
WebKitLoadEvent loadEvent,
|
||||
gpointer user)
|
||||
{
|
||||
GtkChildProcess& owner = *reinterpret_cast<GtkChildProcess*> (user);
|
||||
owner.onLoadChanged (loadEvent);
|
||||
}
|
||||
|
||||
static void loadFailedCallback (WebKitWebView*,
|
||||
WebKitLoadEvent /*loadEvent*/,
|
||||
gchar* /*failing_uri*/,
|
||||
GError* error,
|
||||
gpointer user)
|
||||
{
|
||||
GtkChildProcess& owner = *reinterpret_cast<GtkChildProcess*> (user);
|
||||
owner.onLoadFailed (error);
|
||||
}
|
||||
|
||||
int outChannel;
|
||||
CommandReceiver receiver;
|
||||
WebKitWebView* webview = nullptr;
|
||||
Array<WebKitPolicyDecision*> decisions;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class WebBrowserComponent::Pimpl : private Thread,
|
||||
private CommandReceiver::Responder
|
||||
{
|
||||
public:
|
||||
Pimpl (WebBrowserComponent& parent)
|
||||
: Thread ("Webview"), owner (parent)
|
||||
{}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
quit();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void init()
|
||||
{
|
||||
launchChild();
|
||||
|
||||
int ret = pipe (threadControl);
|
||||
|
||||
ignoreUnused (ret);
|
||||
jassert (ret == 0);
|
||||
|
||||
CommandReceiver::setBlocking (inChannel, true);
|
||||
CommandReceiver::setBlocking (outChannel, true);
|
||||
CommandReceiver::setBlocking (threadControl[0], false);
|
||||
CommandReceiver::setBlocking (threadControl[1], true);
|
||||
|
||||
unsigned long windowHandle;
|
||||
ssize_t actual = read (inChannel, &windowHandle, sizeof (windowHandle));
|
||||
|
||||
if (actual != sizeof (windowHandle))
|
||||
{
|
||||
killChild();
|
||||
return;
|
||||
}
|
||||
|
||||
receiver.reset (new CommandReceiver (this, inChannel));
|
||||
startThread();
|
||||
|
||||
xembed.reset (new XEmbedComponent (windowHandle));
|
||||
owner.addAndMakeVisible (xembed.get());
|
||||
}
|
||||
|
||||
void quit()
|
||||
{
|
||||
if (isThreadRunning())
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
|
||||
char ignore = 0;
|
||||
ssize_t ret;
|
||||
|
||||
do
|
||||
{
|
||||
ret = write (threadControl[1], &ignore, 1);
|
||||
} while (ret == -1 && errno == EINTR);
|
||||
|
||||
waitForThreadToExit (-1);
|
||||
receiver = nullptr;
|
||||
}
|
||||
|
||||
if (childProcess != 0)
|
||||
{
|
||||
CommandReceiver::sendCommand (outChannel, "quit", var());
|
||||
killChild();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData)
|
||||
{
|
||||
DynamicObject::Ptr params = new DynamicObject;
|
||||
|
||||
params->setProperty ("url", url);
|
||||
|
||||
if (headers != nullptr)
|
||||
params->setProperty ("headers", var (*headers));
|
||||
|
||||
if (postData != nullptr)
|
||||
params->setProperty ("postData", var (*postData));
|
||||
|
||||
CommandReceiver::sendCommand (outChannel, "goToURL", var (params));
|
||||
}
|
||||
|
||||
void goBack() { CommandReceiver::sendCommand (outChannel, "goBack", var()); }
|
||||
void goForward() { CommandReceiver::sendCommand (outChannel, "goForward", var()); }
|
||||
void refresh() { CommandReceiver::sendCommand (outChannel, "refresh", var()); }
|
||||
void stop() { CommandReceiver::sendCommand (outChannel, "stop", var()); }
|
||||
|
||||
void resized()
|
||||
{
|
||||
if (xembed != nullptr)
|
||||
xembed->setBounds (owner.getLocalBounds());
|
||||
}
|
||||
private:
|
||||
//==============================================================================
|
||||
void killChild()
|
||||
{
|
||||
if (childProcess != 0)
|
||||
{
|
||||
xembed = nullptr;
|
||||
|
||||
int status = 0, result;
|
||||
|
||||
result = waitpid (childProcess, &status, WNOHANG);
|
||||
for (int i = 0; i < 15 && (! WIFEXITED(status) || result != childProcess); ++i)
|
||||
{
|
||||
Thread::sleep (100);
|
||||
result = waitpid (childProcess, &status, WNOHANG);
|
||||
}
|
||||
|
||||
// clean-up any zombies
|
||||
status = 0;
|
||||
if (! WIFEXITED(status) || result != childProcess)
|
||||
{
|
||||
do
|
||||
{
|
||||
kill (childProcess, SIGTERM);
|
||||
waitpid (childProcess, &status, 0);
|
||||
} while (! WIFEXITED(status));
|
||||
}
|
||||
|
||||
childProcess = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void launchChild()
|
||||
{
|
||||
int ret;
|
||||
int inPipe[2], outPipe[2];
|
||||
|
||||
ret = pipe (inPipe);
|
||||
ignoreUnused (ret); jassert (ret == 0);
|
||||
|
||||
ret = pipe (outPipe);
|
||||
ignoreUnused (ret); jassert (ret == 0);
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0)
|
||||
{
|
||||
close (inPipe[0]);
|
||||
close (outPipe[1]);
|
||||
|
||||
HeapBlock<const char*> argv (5);
|
||||
StringArray arguments;
|
||||
|
||||
arguments.add (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
|
||||
arguments.add ("--juce-gtkwebkitfork-child");
|
||||
arguments.add (String (outPipe[0]));
|
||||
arguments.add (String (inPipe [1]));
|
||||
|
||||
for (int i = 0; i < arguments.size(); ++i)
|
||||
argv[i] = arguments[i].toRawUTF8();
|
||||
|
||||
argv[4] = nullptr;
|
||||
|
||||
#if JUCE_STANDALONE_APPLICATION
|
||||
execv (arguments[0].toRawUTF8(), (char**) argv.getData());
|
||||
#else
|
||||
juce_gtkWebkitMain (4, (const char**) argv.getData());
|
||||
#endif
|
||||
exit (0);
|
||||
}
|
||||
|
||||
close (inPipe[1]);
|
||||
close (outPipe[0]);
|
||||
|
||||
inChannel = inPipe[0];
|
||||
outChannel = outPipe[1];
|
||||
|
||||
childProcess = pid;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
while (! threadShouldExit())
|
||||
{
|
||||
if (shouldExit())
|
||||
return;
|
||||
|
||||
receiver->tryNextRead();
|
||||
|
||||
fd_set set;
|
||||
FD_ZERO (&set);
|
||||
FD_SET (threadControl[0], &set);
|
||||
FD_SET (receiver->getFd(), &set);
|
||||
|
||||
int max_fd = jmax (threadControl[0], receiver->getFd());
|
||||
|
||||
int result = 0;
|
||||
|
||||
while (result == 0 || (result < 0 && errno == EINTR))
|
||||
result = select (max_fd + 1, &set, NULL, NULL, NULL);
|
||||
|
||||
if (result < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldExit()
|
||||
{
|
||||
char ignore;
|
||||
ssize_t result = read (threadControl[0], &ignore, 1);
|
||||
|
||||
return (result != -1 || (errno != EAGAIN && errno != EWOULDBLOCK));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleCommandOnMessageThread (const String& cmd, const var& params)
|
||||
{
|
||||
String url (params.getProperty ("url", var()).toString());
|
||||
|
||||
if (cmd == "pageAboutToLoad") handlePageAboutToLoad (url, params);
|
||||
else if (cmd == "pageFinishedLoading") owner.pageFinishedLoading (url);
|
||||
else if (cmd == "windowCloseRequest") owner.windowCloseRequest();
|
||||
else if (cmd == "newWindowAttemptingToLoad") owner.newWindowAttemptingToLoad (url);
|
||||
else if (cmd == "pageLoadHadNetworkError") handlePageLoadHadNetworkError (params);
|
||||
|
||||
threadBlocker.signal();
|
||||
}
|
||||
|
||||
void handlePageAboutToLoad (const String& url, const var& inputParams)
|
||||
{
|
||||
int64 decision_id = inputParams.getProperty ("decision_id", var (0));
|
||||
|
||||
if (decision_id != 0)
|
||||
{
|
||||
DynamicObject::Ptr params = new DynamicObject;
|
||||
|
||||
params->setProperty ("decision_id", decision_id);
|
||||
params->setProperty ("allow", owner.pageAboutToLoad (url));
|
||||
|
||||
CommandReceiver::sendCommand (outChannel, "decision", var (params));
|
||||
}
|
||||
}
|
||||
|
||||
void handlePageLoadHadNetworkError (const var& params)
|
||||
{
|
||||
String error = params.getProperty ("error", "Unknown error");
|
||||
|
||||
if (owner.pageLoadHadNetworkError (error))
|
||||
goToURL (String ("data:text/plain,") + error, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void handleCommand (const String& cmd, const var& params) override
|
||||
{
|
||||
threadBlocker.reset();
|
||||
|
||||
(new HandleOnMessageThread (this, cmd, params))->post();
|
||||
|
||||
// wait until the command has executed on the message thread
|
||||
// this ensures that Pimpl can never be deleted while the
|
||||
// message has not been executed yet
|
||||
threadBlocker.wait (-1);
|
||||
}
|
||||
|
||||
void receiverHadError() override {}
|
||||
|
||||
//==============================================================================
|
||||
struct HandleOnMessageThread : public CallbackMessage
|
||||
{
|
||||
HandleOnMessageThread (Pimpl* pimpl, const String& cmdToUse, const var& params)
|
||||
: owner (pimpl), cmdToSend (cmdToUse), paramsToSend (params)
|
||||
{}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
owner->handleCommandOnMessageThread (cmdToSend, paramsToSend);
|
||||
}
|
||||
|
||||
Pimpl* owner;
|
||||
String cmdToSend;
|
||||
var paramsToSend;
|
||||
};
|
||||
|
||||
private:
|
||||
WebBrowserComponent& owner;
|
||||
std::unique_ptr<CommandReceiver> receiver;
|
||||
int childProcess = 0, inChannel = 0, outChannel = 0;
|
||||
int threadControl[2];
|
||||
std::unique_ptr<XEmbedComponent> xembed;
|
||||
WaitableEvent threadBlocker;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
|
||||
: browser (new Pimpl (*this)),
|
||||
blankPageShown (false),
|
||||
unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
|
||||
{
|
||||
setOpaque (true);
|
||||
|
||||
browser->init();
|
||||
}
|
||||
|
||||
WebBrowserComponent::~WebBrowserComponent()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::goToURL (const String& url,
|
||||
const StringArray* headers,
|
||||
const MemoryBlock* postData)
|
||||
{
|
||||
lastURL = url;
|
||||
|
||||
if (headers != nullptr)
|
||||
lastHeaders = *headers;
|
||||
else
|
||||
lastHeaders.clear();
|
||||
|
||||
if (postData != nullptr)
|
||||
lastPostData = *postData;
|
||||
else
|
||||
lastPostData.reset();
|
||||
|
||||
blankPageShown = false;
|
||||
|
||||
browser->goToURL (url, headers, postData);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::stop()
|
||||
{
|
||||
browser->stop();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goBack()
|
||||
{
|
||||
lastURL.clear();
|
||||
blankPageShown = false;
|
||||
|
||||
browser->goBack();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::goForward()
|
||||
{
|
||||
lastURL.clear();
|
||||
browser->goForward();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::refresh()
|
||||
{
|
||||
browser->refresh();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void WebBrowserComponent::paint (Graphics& g)
|
||||
{
|
||||
g.fillAll (Colours::white);
|
||||
}
|
||||
|
||||
void WebBrowserComponent::checkWindowAssociation()
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::reloadLastURL()
|
||||
{
|
||||
if (lastURL.isNotEmpty())
|
||||
{
|
||||
goToURL (lastURL, &lastHeaders, &lastPostData);
|
||||
lastURL.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void WebBrowserComponent::parentHierarchyChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::resized()
|
||||
{
|
||||
if (browser != nullptr)
|
||||
browser->resized();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::visibilityChanged()
|
||||
{
|
||||
checkWindowAssociation();
|
||||
}
|
||||
|
||||
void WebBrowserComponent::focusGained (FocusChangeType)
|
||||
{
|
||||
}
|
||||
|
||||
void WebBrowserComponent::clearCookies()
|
||||
{
|
||||
// Currently not implemented on linux as WebBrowserComponent currently does not
|
||||
// store cookies on linux
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
int juce_gtkWebkitMain (int argc, const char* argv[])
|
||||
{
|
||||
if (argc != 4) return -1;
|
||||
|
||||
|
||||
GtkChildProcess child (String (argv[2]).getIntValue(),
|
||||
String (argv[3]).getIntValue());
|
||||
return child.entry();
|
||||
}
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user