The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

822 lines
25KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. extern int juce_gtkWebkitMain (int argc, const char* argv[]);
  22. class CommandReceiver
  23. {
  24. public:
  25. struct Responder
  26. {
  27. virtual ~Responder() {}
  28. virtual void handleCommand (const String& cmd, const var& param) = 0;
  29. virtual void receiverHadError() = 0;
  30. };
  31. CommandReceiver (Responder* responderToUse, int inputChannelToUse)
  32. : responder (responderToUse), inChannel (inputChannelToUse)
  33. {
  34. setBlocking (inChannel, false);
  35. }
  36. static void setBlocking (int fd, bool shouldBlock)
  37. {
  38. int flags = fcntl (fd, F_GETFL);
  39. fcntl (fd, F_SETFL, (shouldBlock ? (flags & ~O_NONBLOCK)
  40. : (flags | O_NONBLOCK)));
  41. }
  42. int getFd() const { return inChannel; }
  43. void tryNextRead()
  44. {
  45. for (;;)
  46. {
  47. size_t len = (receivingLength ? sizeof (size_t) : bufferLength.len);
  48. if (! receivingLength)
  49. buffer.realloc (len);
  50. char* dst = (receivingLength ? bufferLength.data : buffer.getData());
  51. ssize_t actual = read (inChannel, &dst[pos], static_cast<size_t> (len - pos));
  52. if (actual < 0)
  53. {
  54. if (errno == EINTR)
  55. continue;
  56. break;
  57. }
  58. pos += static_cast<size_t> (actual);
  59. if (pos == len)
  60. {
  61. pos = 0;
  62. if (! receivingLength)
  63. parseJSON (String (buffer.getData(), bufferLength.len));
  64. receivingLength = (! receivingLength);
  65. }
  66. }
  67. if (errno != EAGAIN && errno != EWOULDBLOCK && responder != nullptr)
  68. responder->receiverHadError();
  69. }
  70. static void sendCommand (int outChannel, const String& cmd, const var& params)
  71. {
  72. DynamicObject::Ptr obj = new DynamicObject;
  73. obj->setProperty (getCmdIdentifier(), cmd);
  74. if (! params.isVoid())
  75. obj->setProperty (getParamIdentifier(), params);
  76. String json (JSON::toString (var (obj.get())));
  77. size_t jsonLength = static_cast<size_t> (json.length());
  78. size_t len = sizeof (size_t) + jsonLength;
  79. HeapBlock<char> buffer (len);
  80. char* dst = buffer.getData();
  81. memcpy (dst, &jsonLength, sizeof (size_t));
  82. dst += sizeof (size_t);
  83. memcpy (dst, json.toRawUTF8(), jsonLength);
  84. ssize_t ret;
  85. do
  86. {
  87. ret = write (outChannel, buffer.getData(), len);
  88. } while (ret == -1 && errno == EINTR);
  89. }
  90. private:
  91. void parseJSON (const String& json)
  92. {
  93. var object (JSON::fromString (json));
  94. if (! object.isVoid())
  95. {
  96. String cmd (object.getProperty (getCmdIdentifier(), var()).toString());
  97. var params (object.getProperty (getParamIdentifier(), var()));
  98. if (responder != nullptr)
  99. responder->handleCommand (cmd, params);
  100. }
  101. }
  102. static Identifier getCmdIdentifier() { static Identifier Id ("cmd"); return Id; }
  103. static Identifier getParamIdentifier() { static Identifier Id ("params"); return Id; }
  104. Responder* responder;
  105. int inChannel;
  106. size_t pos = 0;
  107. bool receivingLength = true;
  108. union { char data [sizeof (size_t)]; size_t len; } bufferLength;
  109. HeapBlock<char> buffer;
  110. };
  111. //==============================================================================
  112. class GtkChildProcess : private CommandReceiver::Responder
  113. {
  114. public:
  115. //==============================================================================
  116. GtkChildProcess (int inChannel, int outChannelToUse)
  117. : outChannel (outChannelToUse), receiver (this, inChannel)
  118. {}
  119. typedef void (*SetHardwareAcclPolicyFunctionPtr) (WebKitSettings*, int);
  120. int entry()
  121. {
  122. CommandReceiver::setBlocking (outChannel, true);
  123. gtk_init (nullptr, nullptr);
  124. WebKitSettings* settings = webkit_settings_new();
  125. // webkit_settings_set_hardware_acceleration_policy was only added recently to webkit2
  126. // but is needed when running a WebBrowserComponent in a Parallels VM with 3D acceleration enabled
  127. auto setHardwarePolicy
  128. = reinterpret_cast<SetHardwareAcclPolicyFunctionPtr> (dlsym (RTLD_DEFAULT, "webkit_settings_set_hardware_acceleration_policy"));
  129. if (setHardwarePolicy != nullptr)
  130. setHardwarePolicy (settings, 2 /*WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER*/);
  131. GtkWidget *plug;
  132. plug = gtk_plug_new(0);
  133. GtkWidget* container;
  134. container = gtk_scrolled_window_new (nullptr, nullptr);
  135. GtkWidget* webviewWidget = webkit_web_view_new_with_settings (settings);
  136. webview = WEBKIT_WEB_VIEW (webviewWidget);
  137. gtk_container_add (GTK_CONTAINER (container), webviewWidget);
  138. gtk_container_add (GTK_CONTAINER (plug), container);
  139. webkit_web_view_load_uri (webview, "about:blank");
  140. g_signal_connect (webview, "decide-policy",
  141. G_CALLBACK (decidePolicyCallback), this);
  142. g_signal_connect (webview, "load-changed",
  143. G_CALLBACK (loadChangedCallback), this);
  144. g_signal_connect (webview, "load-failed",
  145. G_CALLBACK (loadFailedCallback), this);
  146. gtk_widget_show_all (plug);
  147. unsigned long wID = (unsigned long) gtk_plug_get_id (GTK_PLUG (plug));
  148. ssize_t ret;
  149. do {
  150. ret = write (outChannel, &wID, sizeof (wID));
  151. } while (ret == -1 && errno == EINTR);
  152. g_unix_fd_add (receiver.getFd(), G_IO_IN, pipeReadyStatic, this);
  153. receiver.tryNextRead();
  154. gtk_main();
  155. return 0;
  156. }
  157. void goToURL (const var& params)
  158. {
  159. static Identifier urlIdentifier ("url");
  160. String url (params.getProperty (urlIdentifier, var()).toString());
  161. webkit_web_view_load_uri (webview, url.toRawUTF8());
  162. }
  163. void handleDecisionResponse (const var& params)
  164. {
  165. WebKitPolicyDecision* decision
  166. = (WebKitPolicyDecision*) ((int64) params.getProperty ("decision_id", var (0)));
  167. bool allow = params.getProperty ("allow", var (false));
  168. if (decision != nullptr && decisions.contains (decision))
  169. {
  170. if (allow)
  171. webkit_policy_decision_use (decision);
  172. else
  173. webkit_policy_decision_ignore (decision);
  174. decisions.removeAllInstancesOf (decision);
  175. g_object_unref (decision);
  176. }
  177. }
  178. //==============================================================================
  179. void handleCommand (const String& cmd, const var& params) override
  180. {
  181. if (cmd == "quit") quit();
  182. else if (cmd == "goToURL") goToURL (params);
  183. else if (cmd == "goBack") webkit_web_view_go_back (webview);
  184. else if (cmd == "goForward") webkit_web_view_go_forward (webview);
  185. else if (cmd == "refresh") webkit_web_view_reload (webview);
  186. else if (cmd == "stop") webkit_web_view_stop_loading (webview);
  187. else if (cmd == "decision") handleDecisionResponse (params);
  188. }
  189. void receiverHadError() override
  190. {
  191. exit (-1);
  192. }
  193. //==============================================================================
  194. bool pipeReady (gint fd, GIOCondition)
  195. {
  196. if (fd == receiver.getFd())
  197. {
  198. receiver.tryNextRead();
  199. return true;
  200. }
  201. return false;
  202. }
  203. void quit()
  204. {
  205. gtk_main_quit();
  206. }
  207. bool onNavigation (String frameName,
  208. WebKitNavigationAction* action,
  209. WebKitPolicyDecision* decision)
  210. {
  211. if (decision != nullptr && frameName.isEmpty())
  212. {
  213. g_object_ref (decision);
  214. decisions.add (decision);
  215. DynamicObject::Ptr params = new DynamicObject;
  216. params->setProperty ("url", String (webkit_uri_request_get_uri (webkit_navigation_action_get_request (action))));
  217. params->setProperty ("decision_id", (int64) decision);
  218. CommandReceiver::sendCommand (outChannel, "pageAboutToLoad", var (params.get()));
  219. return true;
  220. }
  221. return false;
  222. }
  223. bool onNewWindow (String /*frameName*/,
  224. WebKitNavigationAction* action,
  225. WebKitPolicyDecision* decision)
  226. {
  227. if (decision != nullptr)
  228. {
  229. DynamicObject::Ptr params = new DynamicObject;
  230. params->setProperty ("url", String (webkit_uri_request_get_uri (webkit_navigation_action_get_request (action))));
  231. CommandReceiver::sendCommand (outChannel, "newWindowAttemptingToLoad", var (params.get()));
  232. // never allow new windows
  233. webkit_policy_decision_ignore (decision);
  234. return true;
  235. }
  236. return false;
  237. }
  238. void onLoadChanged (WebKitLoadEvent loadEvent)
  239. {
  240. if (loadEvent == WEBKIT_LOAD_FINISHED)
  241. {
  242. DynamicObject::Ptr params = new DynamicObject;
  243. params->setProperty ("url", String (webkit_web_view_get_uri (webview)));
  244. CommandReceiver::sendCommand (outChannel, "pageFinishedLoading", var (params.get()));
  245. }
  246. }
  247. bool onDecidePolicy (WebKitPolicyDecision* decision,
  248. WebKitPolicyDecisionType decisionType)
  249. {
  250. switch (decisionType)
  251. {
  252. case WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION:
  253. {
  254. WebKitNavigationPolicyDecision* navigationDecision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
  255. const char* frameName = webkit_navigation_policy_decision_get_frame_name (navigationDecision);
  256. return onNavigation (String (frameName != nullptr ? frameName : ""),
  257. webkit_navigation_policy_decision_get_navigation_action (navigationDecision),
  258. decision);
  259. }
  260. break;
  261. case WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION:
  262. {
  263. WebKitNavigationPolicyDecision* navigationDecision = WEBKIT_NAVIGATION_POLICY_DECISION (decision);
  264. const char* frameName = webkit_navigation_policy_decision_get_frame_name (navigationDecision);
  265. return onNewWindow (String (frameName != nullptr ? frameName : ""),
  266. webkit_navigation_policy_decision_get_navigation_action (navigationDecision),
  267. decision);
  268. }
  269. break;
  270. case WEBKIT_POLICY_DECISION_TYPE_RESPONSE:
  271. {
  272. WebKitResponsePolicyDecision *response = WEBKIT_RESPONSE_POLICY_DECISION (decision);
  273. // for now just always allow response requests
  274. ignoreUnused (response);
  275. webkit_policy_decision_use (decision);
  276. return true;
  277. }
  278. break;
  279. default:
  280. break;
  281. }
  282. return false;
  283. }
  284. void onLoadFailed (GError* error)
  285. {
  286. DynamicObject::Ptr params = new DynamicObject;
  287. params->setProperty ("error", String (error != nullptr ? error->message : "unknown error"));
  288. CommandReceiver::sendCommand (outChannel, "pageLoadHadNetworkError", var (params.get()));
  289. }
  290. private:
  291. static gboolean pipeReadyStatic (gint fd, GIOCondition condition, gpointer user)
  292. {
  293. return (reinterpret_cast<GtkChildProcess*> (user)->pipeReady (fd, condition) ? TRUE : FALSE);
  294. }
  295. static gboolean decidePolicyCallback (WebKitWebView*,
  296. WebKitPolicyDecision* decision,
  297. WebKitPolicyDecisionType decisionType,
  298. gpointer user)
  299. {
  300. GtkChildProcess& owner = *reinterpret_cast<GtkChildProcess*> (user);
  301. return (owner.onDecidePolicy (decision, decisionType) ? TRUE : FALSE);
  302. }
  303. static void loadChangedCallback (WebKitWebView*,
  304. WebKitLoadEvent loadEvent,
  305. gpointer user)
  306. {
  307. GtkChildProcess& owner = *reinterpret_cast<GtkChildProcess*> (user);
  308. owner.onLoadChanged (loadEvent);
  309. }
  310. static void loadFailedCallback (WebKitWebView*,
  311. WebKitLoadEvent /*loadEvent*/,
  312. gchar* /*failing_uri*/,
  313. GError* error,
  314. gpointer user)
  315. {
  316. GtkChildProcess& owner = *reinterpret_cast<GtkChildProcess*> (user);
  317. owner.onLoadFailed (error);
  318. }
  319. int outChannel;
  320. CommandReceiver receiver;
  321. WebKitWebView* webview = nullptr;
  322. Array<WebKitPolicyDecision*> decisions;
  323. };
  324. //==============================================================================
  325. class WebBrowserComponent::Pimpl : private Thread,
  326. private CommandReceiver::Responder
  327. {
  328. public:
  329. Pimpl (WebBrowserComponent& parent)
  330. : Thread ("Webview"), owner (parent)
  331. {}
  332. ~Pimpl() override
  333. {
  334. quit();
  335. }
  336. //==============================================================================
  337. void init()
  338. {
  339. launchChild();
  340. int ret = pipe (threadControl);
  341. ignoreUnused (ret);
  342. jassert (ret == 0);
  343. CommandReceiver::setBlocking (inChannel, true);
  344. CommandReceiver::setBlocking (outChannel, true);
  345. CommandReceiver::setBlocking (threadControl[0], false);
  346. CommandReceiver::setBlocking (threadControl[1], true);
  347. unsigned long windowHandle;
  348. ssize_t actual = read (inChannel, &windowHandle, sizeof (windowHandle));
  349. if (actual != (ssize_t) sizeof (windowHandle))
  350. {
  351. killChild();
  352. return;
  353. }
  354. receiver.reset (new CommandReceiver (this, inChannel));
  355. pfds.push_back ({ threadControl[0], POLLIN, 0 });
  356. pfds.push_back ({ receiver->getFd(), POLLIN, 0 });
  357. startThread();
  358. xembed.reset (new XEmbedComponent (windowHandle));
  359. owner.addAndMakeVisible (xembed.get());
  360. }
  361. void quit()
  362. {
  363. if (isThreadRunning())
  364. {
  365. signalThreadShouldExit();
  366. char ignore = 0;
  367. ssize_t ret;
  368. do
  369. {
  370. ret = write (threadControl[1], &ignore, 1);
  371. } while (ret == -1 && errno == EINTR);
  372. waitForThreadToExit (-1);
  373. receiver = nullptr;
  374. }
  375. if (childProcess != 0)
  376. {
  377. CommandReceiver::sendCommand (outChannel, "quit", var());
  378. killChild();
  379. }
  380. }
  381. //==============================================================================
  382. void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData)
  383. {
  384. DynamicObject::Ptr params = new DynamicObject;
  385. params->setProperty ("url", url);
  386. if (headers != nullptr)
  387. params->setProperty ("headers", var (*headers));
  388. if (postData != nullptr)
  389. params->setProperty ("postData", var (*postData));
  390. CommandReceiver::sendCommand (outChannel, "goToURL", var (params.get()));
  391. }
  392. void goBack() { CommandReceiver::sendCommand (outChannel, "goBack", var()); }
  393. void goForward() { CommandReceiver::sendCommand (outChannel, "goForward", var()); }
  394. void refresh() { CommandReceiver::sendCommand (outChannel, "refresh", var()); }
  395. void stop() { CommandReceiver::sendCommand (outChannel, "stop", var()); }
  396. void resized()
  397. {
  398. if (xembed != nullptr)
  399. xembed->setBounds (owner.getLocalBounds());
  400. }
  401. private:
  402. //==============================================================================
  403. void killChild()
  404. {
  405. if (childProcess != 0)
  406. {
  407. xembed = nullptr;
  408. int status = 0, result;
  409. result = waitpid (childProcess, &status, WNOHANG);
  410. for (int i = 0; i < 15 && (! WIFEXITED(status) || result != childProcess); ++i)
  411. {
  412. Thread::sleep (100);
  413. result = waitpid (childProcess, &status, WNOHANG);
  414. }
  415. // clean-up any zombies
  416. status = 0;
  417. if (! WIFEXITED(status) || result != childProcess)
  418. {
  419. do
  420. {
  421. kill (childProcess, SIGTERM);
  422. waitpid (childProcess, &status, 0);
  423. } while (! WIFEXITED(status));
  424. }
  425. childProcess = 0;
  426. }
  427. }
  428. void launchChild()
  429. {
  430. int ret;
  431. int inPipe[2], outPipe[2];
  432. ret = pipe (inPipe);
  433. ignoreUnused (ret); jassert (ret == 0);
  434. ret = pipe (outPipe);
  435. ignoreUnused (ret); jassert (ret == 0);
  436. int pid = fork();
  437. if (pid == 0)
  438. {
  439. close (inPipe[0]);
  440. close (outPipe[1]);
  441. HeapBlock<const char*> argv (5);
  442. StringArray arguments;
  443. arguments.add (File::getSpecialLocation (File::currentExecutableFile).getFullPathName());
  444. arguments.add ("--juce-gtkwebkitfork-child");
  445. arguments.add (String (outPipe[0]));
  446. arguments.add (String (inPipe [1]));
  447. for (int i = 0; i < arguments.size(); ++i)
  448. argv[i] = arguments[i].toRawUTF8();
  449. argv[4] = nullptr;
  450. #if JUCE_STANDALONE_APPLICATION
  451. execv (arguments[0].toRawUTF8(), (char**) argv.getData());
  452. #else
  453. juce_gtkWebkitMain (4, (const char**) argv.getData());
  454. #endif
  455. exit (0);
  456. }
  457. close (inPipe[1]);
  458. close (outPipe[0]);
  459. inChannel = inPipe[0];
  460. outChannel = outPipe[1];
  461. childProcess = pid;
  462. }
  463. void run() override
  464. {
  465. while (! threadShouldExit())
  466. {
  467. if (shouldExit())
  468. return;
  469. receiver->tryNextRead();
  470. int result = 0;
  471. while (result == 0 || (result < 0 && errno == EINTR))
  472. result = poll (&pfds.front(), static_cast<nfds_t> (pfds.size()), 0);
  473. if (result < 0)
  474. break;
  475. }
  476. }
  477. bool shouldExit()
  478. {
  479. char ignore;
  480. ssize_t result = read (threadControl[0], &ignore, 1);
  481. return (result != -1 || (errno != EAGAIN && errno != EWOULDBLOCK));
  482. }
  483. //==============================================================================
  484. void handleCommandOnMessageThread (const String& cmd, const var& params)
  485. {
  486. String url (params.getProperty ("url", var()).toString());
  487. if (cmd == "pageAboutToLoad") handlePageAboutToLoad (url, params);
  488. else if (cmd == "pageFinishedLoading") owner.pageFinishedLoading (url);
  489. else if (cmd == "windowCloseRequest") owner.windowCloseRequest();
  490. else if (cmd == "newWindowAttemptingToLoad") owner.newWindowAttemptingToLoad (url);
  491. else if (cmd == "pageLoadHadNetworkError") handlePageLoadHadNetworkError (params);
  492. threadBlocker.signal();
  493. }
  494. void handlePageAboutToLoad (const String& url, const var& inputParams)
  495. {
  496. int64 decision_id = inputParams.getProperty ("decision_id", var (0));
  497. if (decision_id != 0)
  498. {
  499. DynamicObject::Ptr params = new DynamicObject;
  500. params->setProperty ("decision_id", decision_id);
  501. params->setProperty ("allow", owner.pageAboutToLoad (url));
  502. CommandReceiver::sendCommand (outChannel, "decision", var (params.get()));
  503. }
  504. }
  505. void handlePageLoadHadNetworkError (const var& params)
  506. {
  507. String error = params.getProperty ("error", "Unknown error");
  508. if (owner.pageLoadHadNetworkError (error))
  509. goToURL (String ("data:text/plain,") + error, nullptr, nullptr);
  510. }
  511. void handleCommand (const String& cmd, const var& params) override
  512. {
  513. threadBlocker.reset();
  514. (new HandleOnMessageThread (this, cmd, params))->post();
  515. // wait until the command has executed on the message thread
  516. // this ensures that Pimpl can never be deleted while the
  517. // message has not been executed yet
  518. threadBlocker.wait (-1);
  519. }
  520. void receiverHadError() override {}
  521. //==============================================================================
  522. struct HandleOnMessageThread : public CallbackMessage
  523. {
  524. HandleOnMessageThread (Pimpl* pimpl, const String& cmdToUse, const var& params)
  525. : owner (pimpl), cmdToSend (cmdToUse), paramsToSend (params)
  526. {}
  527. void messageCallback() override
  528. {
  529. owner->handleCommandOnMessageThread (cmdToSend, paramsToSend);
  530. }
  531. Pimpl* owner;
  532. String cmdToSend;
  533. var paramsToSend;
  534. };
  535. private:
  536. WebBrowserComponent& owner;
  537. std::unique_ptr<CommandReceiver> receiver;
  538. int childProcess = 0, inChannel = 0, outChannel = 0;
  539. int threadControl[2];
  540. std::unique_ptr<XEmbedComponent> xembed;
  541. WaitableEvent threadBlocker;
  542. std::vector<pollfd> pfds;
  543. };
  544. //==============================================================================
  545. WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
  546. : browser (new Pimpl (*this)),
  547. unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
  548. {
  549. ignoreUnused (blankPageShown);
  550. ignoreUnused (unloadPageWhenBrowserIsHidden);
  551. setOpaque (true);
  552. browser->init();
  553. }
  554. WebBrowserComponent::~WebBrowserComponent()
  555. {
  556. }
  557. //==============================================================================
  558. void WebBrowserComponent::goToURL (const String& url,
  559. const StringArray* headers,
  560. const MemoryBlock* postData)
  561. {
  562. lastURL = url;
  563. if (headers != nullptr)
  564. lastHeaders = *headers;
  565. else
  566. lastHeaders.clear();
  567. if (postData != nullptr)
  568. lastPostData = *postData;
  569. else
  570. lastPostData.reset();
  571. browser->goToURL (url, headers, postData);
  572. }
  573. void WebBrowserComponent::stop()
  574. {
  575. browser->stop();
  576. }
  577. void WebBrowserComponent::goBack()
  578. {
  579. lastURL.clear();
  580. browser->goBack();
  581. }
  582. void WebBrowserComponent::goForward()
  583. {
  584. lastURL.clear();
  585. browser->goForward();
  586. }
  587. void WebBrowserComponent::refresh()
  588. {
  589. browser->refresh();
  590. }
  591. //==============================================================================
  592. void WebBrowserComponent::paint (Graphics& g)
  593. {
  594. g.fillAll (Colours::white);
  595. }
  596. void WebBrowserComponent::checkWindowAssociation()
  597. {
  598. }
  599. void WebBrowserComponent::reloadLastURL()
  600. {
  601. if (lastURL.isNotEmpty())
  602. {
  603. goToURL (lastURL, &lastHeaders, &lastPostData);
  604. lastURL.clear();
  605. }
  606. }
  607. void WebBrowserComponent::parentHierarchyChanged()
  608. {
  609. checkWindowAssociation();
  610. }
  611. void WebBrowserComponent::resized()
  612. {
  613. if (browser != nullptr)
  614. browser->resized();
  615. }
  616. void WebBrowserComponent::visibilityChanged()
  617. {
  618. checkWindowAssociation();
  619. }
  620. void WebBrowserComponent::focusGained (FocusChangeType)
  621. {
  622. }
  623. void WebBrowserComponent::clearCookies()
  624. {
  625. // Currently not implemented on linux as WebBrowserComponent currently does not
  626. // store cookies on linux
  627. jassertfalse;
  628. }
  629. int juce_gtkWebkitMain (int argc, const char* argv[])
  630. {
  631. if (argc != 4) return -1;
  632. GtkChildProcess child (String (argv[2]).getIntValue(),
  633. String (argv[3]).getIntValue());
  634. return child.entry();
  635. }
  636. } // namespace juce