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.

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