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.

753 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. static NSURL* appendParametersToFileURL (const URL& url, NSURL* fileUrl)
  21. {
  22. if (@available (macOS 10.10, *))
  23. {
  24. const auto parameterNames = url.getParameterNames();
  25. const auto parameterValues = url.getParameterValues();
  26. jassert (parameterNames.size() == parameterValues.size());
  27. if (parameterNames.isEmpty())
  28. return fileUrl;
  29. NSUniquePtr<NSURLComponents> components ([[NSURLComponents alloc] initWithURL: fileUrl resolvingAgainstBaseURL: NO]);
  30. NSUniquePtr<NSMutableArray> queryItems ([[NSMutableArray alloc] init]);
  31. for (int i = 0; i < parameterNames.size(); ++i)
  32. [queryItems.get() addObject: [NSURLQueryItem queryItemWithName: juceStringToNS (parameterNames[i])
  33. value: juceStringToNS (parameterValues[i])]];
  34. [components.get() setQueryItems: queryItems.get()];
  35. return [components.get() URL];
  36. }
  37. const auto queryString = url.getQueryString();
  38. if (queryString.isNotEmpty())
  39. if (NSString* fileUrlString = [fileUrl absoluteString])
  40. return [NSURL URLWithString: [fileUrlString stringByAppendingString: juceStringToNS (queryString)]];
  41. return fileUrl;
  42. }
  43. static NSMutableURLRequest* getRequestForURL (const String& url, const StringArray* headers, const MemoryBlock* postData)
  44. {
  45. NSString* urlString = juceStringToNS (url);
  46. if (@available (macOS 10.9, *))
  47. {
  48. urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters: [NSCharacterSet URLQueryAllowedCharacterSet]];
  49. }
  50. else
  51. {
  52. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  53. urlString = [urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
  54. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  55. }
  56. if (NSURL* nsURL = [NSURL URLWithString: urlString])
  57. {
  58. NSMutableURLRequest* r
  59. = [NSMutableURLRequest requestWithURL: nsURL
  60. cachePolicy: NSURLRequestUseProtocolCachePolicy
  61. timeoutInterval: 30.0];
  62. if (postData != nullptr && postData->getSize() > 0)
  63. {
  64. [r setHTTPMethod: nsStringLiteral ("POST")];
  65. [r setHTTPBody: [NSData dataWithBytes: postData->getData()
  66. length: postData->getSize()]];
  67. }
  68. if (headers != nullptr)
  69. {
  70. for (int i = 0; i < headers->size(); ++i)
  71. {
  72. auto headerName = (*headers)[i].upToFirstOccurrenceOf (":", false, false).trim();
  73. auto headerValue = (*headers)[i].fromFirstOccurrenceOf (":", false, false).trim();
  74. [r setValue: juceStringToNS (headerValue)
  75. forHTTPHeaderField: juceStringToNS (headerName)];
  76. }
  77. }
  78. return r;
  79. }
  80. return nullptr;
  81. }
  82. #if JUCE_MAC
  83. template <class WebViewClass>
  84. struct WebViewKeyEquivalentResponder final : public ObjCClass<WebViewClass>
  85. {
  86. WebViewKeyEquivalentResponder()
  87. : ObjCClass<WebViewClass> ("WebViewKeyEquivalentResponder_")
  88. {
  89. this->addMethod (@selector (performKeyEquivalent:),
  90. [] (id self, SEL selector, NSEvent* event)
  91. {
  92. const auto isCommandDown = [event]
  93. {
  94. const auto modifierFlags = [event modifierFlags];
  95. if (@available (macOS 10.12, *))
  96. return (modifierFlags & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand;
  97. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  98. return (modifierFlags & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask;
  99. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  100. }();
  101. if (isCommandDown)
  102. {
  103. auto sendAction = [&] (SEL actionSelector) -> BOOL
  104. {
  105. return [NSApp sendAction:actionSelector
  106. to:[[self window] firstResponder]
  107. from:self];
  108. };
  109. if ([[event charactersIgnoringModifiers] isEqualToString:@"x"])
  110. return sendAction (@selector (cut:));
  111. if ([[event charactersIgnoringModifiers] isEqualToString:@"c"])
  112. return sendAction (@selector (copy:));
  113. if ([[event charactersIgnoringModifiers] isEqualToString:@"v"])
  114. return sendAction (@selector (paste:));
  115. if ([[event charactersIgnoringModifiers] isEqualToString:@"a"])
  116. return sendAction (@selector (selectAll:));
  117. }
  118. return ObjCClass<WebViewClass>::template sendSuperclassMessage<BOOL> (self, selector, event);
  119. });
  120. this->registerClass();
  121. }
  122. };
  123. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  124. struct DownloadClickDetectorClass final : public ObjCClass<NSObject>
  125. {
  126. DownloadClickDetectorClass() : ObjCClass ("JUCEWebClickDetector_")
  127. {
  128. addIvar<WebBrowserComponent*> ("owner");
  129. addMethod (@selector (webView:didFailLoadWithError:forFrame:), didFailLoadWithError);
  130. addMethod (@selector (webView:didFailProvisionalLoadWithError:forFrame:), didFailLoadWithError);
  131. addMethod (@selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:),
  132. [] (id self, SEL, WebView*, NSDictionary* actionInformation, NSURLRequest*, WebFrame*, id<WebPolicyDecisionListener> listener)
  133. {
  134. if (getOwner (self)->pageAboutToLoad (getOriginalURL (actionInformation)))
  135. [listener use];
  136. else
  137. [listener ignore];
  138. });
  139. addMethod (@selector (webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:),
  140. [] (id self, SEL, WebView*, NSDictionary* actionInformation, NSURLRequest*, NSString*, id<WebPolicyDecisionListener> listener)
  141. {
  142. getOwner (self)->newWindowAttemptingToLoad (getOriginalURL (actionInformation));
  143. [listener ignore];
  144. });
  145. addMethod (@selector (webView:didFinishLoadForFrame:),
  146. [] (id self, SEL, WebView* sender, WebFrame* frame)
  147. {
  148. if ([frame isEqual:[sender mainFrame]])
  149. {
  150. NSURL* url = [[[frame dataSource] request] URL];
  151. getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString]));
  152. }
  153. });
  154. addMethod (@selector (webView:willCloseFrame:),
  155. [] (id self, SEL, WebView*, WebFrame*)
  156. {
  157. getOwner (self)->windowCloseRequest();
  158. });
  159. addMethod (@selector (webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:),
  160. [] (id, SEL, WebView*, id<WebOpenPanelResultListener> resultListener, BOOL allowMultipleFiles)
  161. {
  162. struct DeletedFileChooserWrapper final : private DeletedAtShutdown
  163. {
  164. DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, id<WebOpenPanelResultListener> rl)
  165. : chooser (std::move (fc)), listener (rl)
  166. {
  167. [listener.get() retain];
  168. }
  169. std::unique_ptr<FileChooser> chooser;
  170. ObjCObjectHandle<id<WebOpenPanelResultListener>> listener;
  171. };
  172. auto chooser = std::make_unique<FileChooser> (TRANS ("Select the file you want to upload..."),
  173. File::getSpecialLocation (File::userHomeDirectory),
  174. "*");
  175. auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), resultListener);
  176. auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
  177. | (allowMultipleFiles ? FileBrowserComponent::canSelectMultipleItems : 0);
  178. wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
  179. {
  180. for (auto& f : wrapper->chooser->getResults())
  181. [wrapper->listener.get() chooseFilename: juceStringToNS (f.getFullPathName())];
  182. delete wrapper;
  183. });
  184. });
  185. registerClass();
  186. }
  187. static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); }
  188. static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); }
  189. private:
  190. static String getOriginalURL (NSDictionary* actionInformation)
  191. {
  192. if (NSURL* url = [actionInformation valueForKey: nsStringLiteral ("WebActionOriginalURLKey")])
  193. return nsStringToJuce ([url absoluteString]);
  194. return {};
  195. }
  196. static void didFailLoadWithError (id self, SEL, WebView* sender, NSError* error, WebFrame* frame)
  197. {
  198. if ([frame isEqual: [sender mainFrame]] && error != nullptr && [error code] != NSURLErrorCancelled)
  199. {
  200. auto errorString = nsStringToJuce ([error localizedDescription]);
  201. bool proceedToErrorPage = getOwner (self)->pageLoadHadNetworkError (errorString);
  202. // WebKit doesn't have an internal error page, so make a really simple one ourselves
  203. if (proceedToErrorPage)
  204. getOwner (self)->goToURL ("data:text/plain;charset=UTF-8," + errorString);
  205. }
  206. }
  207. };
  208. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  209. #endif
  210. struct API_AVAILABLE (macos (10.10)) WebViewDelegateClass final : public ObjCClass<NSObject>
  211. {
  212. WebViewDelegateClass() : ObjCClass ("JUCEWebViewDelegate_")
  213. {
  214. addIvar<WebBrowserComponent*> ("owner");
  215. addMethod (@selector (webView:decidePolicyForNavigationAction:decisionHandler:),
  216. [] (id self, SEL, WKWebView*, WKNavigationAction* navigationAction, void (^decisionHandler) (WKNavigationActionPolicy))
  217. {
  218. if (getOwner (self)->pageAboutToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString])))
  219. decisionHandler (WKNavigationActionPolicyAllow);
  220. else
  221. decisionHandler (WKNavigationActionPolicyCancel);
  222. });
  223. addMethod (@selector (webView:didFinishNavigation:),
  224. [] (id self, SEL, WKWebView* webview, WKNavigation*)
  225. {
  226. getOwner (self)->pageFinishedLoading (nsStringToJuce ([[webview URL] absoluteString]));
  227. });
  228. addMethod (@selector (webView:didFailNavigation:withError:),
  229. [] (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
  230. {
  231. displayError (getOwner (self), error);
  232. });
  233. addMethod (@selector (webView:didFailProvisionalNavigation:withError:),
  234. [] (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
  235. {
  236. displayError (getOwner (self), error);
  237. });
  238. addMethod (@selector (webViewDidClose:),
  239. [] (id self, SEL, WKWebView*)
  240. {
  241. getOwner (self)->windowCloseRequest();
  242. });
  243. addMethod (@selector (webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:),
  244. [] (id self, SEL, WKWebView*, WKWebViewConfiguration*, WKNavigationAction* navigationAction, WKWindowFeatures*)
  245. {
  246. getOwner (self)->newWindowAttemptingToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString]));
  247. return nil;
  248. });
  249. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  250. if (@available (macOS 10.12, *))
  251. {
  252. addMethod (@selector (webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:),
  253. [] (id, SEL, WKWebView*, WKOpenPanelParameters* parameters, WKFrameInfo*, void (^completionHandler)(NSArray<NSURL*>*))
  254. {
  255. using CompletionHandlerType = decltype (completionHandler);
  256. class DeletedFileChooserWrapper final : private DeletedAtShutdown
  257. {
  258. public:
  259. DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, CompletionHandlerType h)
  260. : chooser (std::move (fc)), handler (h)
  261. {
  262. [handler.get() retain];
  263. }
  264. ~DeletedFileChooserWrapper()
  265. {
  266. callHandler (nullptr);
  267. }
  268. void callHandler (NSArray<NSURL*>* urls)
  269. {
  270. if (handlerCalled)
  271. return;
  272. handler.get() (urls);
  273. handlerCalled = true;
  274. }
  275. std::unique_ptr<FileChooser> chooser;
  276. private:
  277. ObjCObjectHandle<CompletionHandlerType> handler;
  278. bool handlerCalled = false;
  279. };
  280. auto chooser = std::make_unique<FileChooser> (TRANS ("Select the file you want to upload..."),
  281. File::getSpecialLocation (File::userHomeDirectory), "*");
  282. auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), completionHandler);
  283. auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
  284. | ([parameters allowsMultipleSelection] ? FileBrowserComponent::canSelectMultipleItems : 0);
  285. #if JUCE_MAC
  286. if (@available (macOS 10.14, *))
  287. {
  288. if ([parameters allowsDirectories])
  289. flags |= FileBrowserComponent::canSelectDirectories;
  290. }
  291. #endif
  292. wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
  293. {
  294. auto results = wrapper->chooser->getResults();
  295. auto urls = [NSMutableArray arrayWithCapacity: (NSUInteger) results.size()];
  296. for (auto& f : results)
  297. [urls addObject: [NSURL fileURLWithPath: juceStringToNS (f.getFullPathName())]];
  298. wrapper->callHandler (urls);
  299. delete wrapper;
  300. });
  301. });
  302. }
  303. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  304. registerClass();
  305. }
  306. static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); }
  307. static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); }
  308. private:
  309. static void displayError (WebBrowserComponent* owner, NSError* error)
  310. {
  311. if ([error code] != NSURLErrorCancelled)
  312. {
  313. auto errorString = nsStringToJuce ([error localizedDescription]);
  314. bool proceedToErrorPage = owner->pageLoadHadNetworkError (errorString);
  315. // WKWebView doesn't have an internal error page, so make a really simple one ourselves
  316. if (proceedToErrorPage)
  317. owner->goToURL ("data:text/plain;charset=UTF-8," + errorString);
  318. }
  319. }
  320. };
  321. //==============================================================================
  322. struct WebViewBase
  323. {
  324. virtual ~WebViewBase() = default;
  325. virtual void goToURL (const String&, const StringArray*, const MemoryBlock*) = 0;
  326. virtual void goBack() = 0;
  327. virtual void goForward() = 0;
  328. virtual void stop() = 0;
  329. virtual void refresh() = 0;
  330. virtual id getWebView() = 0;
  331. };
  332. #if JUCE_MAC
  333. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  334. class WebViewImpl : public WebViewBase
  335. {
  336. public:
  337. WebViewImpl (WebBrowserComponent* owner, const String& userAgent)
  338. {
  339. static WebViewKeyEquivalentResponder<WebView> webviewClass;
  340. webView.reset ([webviewClass.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
  341. frameName: nsEmptyString()
  342. groupName: nsEmptyString()]);
  343. webView.get().customUserAgent = juceStringToNS (userAgent);
  344. static DownloadClickDetectorClass cls;
  345. clickListener.reset ([cls.createInstance() init]);
  346. DownloadClickDetectorClass::setOwner (clickListener.get(), owner);
  347. [webView.get() setPolicyDelegate: clickListener.get()];
  348. [webView.get() setFrameLoadDelegate: clickListener.get()];
  349. [webView.get() setUIDelegate: clickListener.get()];
  350. }
  351. ~WebViewImpl() override
  352. {
  353. [webView.get() setPolicyDelegate: nil];
  354. [webView.get() setFrameLoadDelegate: nil];
  355. [webView.get() setUIDelegate: nil];
  356. }
  357. void goToURL (const String& url,
  358. const StringArray* headers,
  359. const MemoryBlock* postData) override
  360. {
  361. if (url.trimStart().startsWithIgnoreCase ("javascript:"))
  362. {
  363. [webView.get() stringByEvaluatingJavaScriptFromString: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))];
  364. return;
  365. }
  366. stop();
  367. auto getRequest = [&]() -> NSMutableURLRequest*
  368. {
  369. if (url.trimStart().startsWithIgnoreCase ("file:"))
  370. {
  371. auto file = URL (url).getLocalFile();
  372. if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())])
  373. return [NSMutableURLRequest requestWithURL: appendParametersToFileURL (url, nsUrl)
  374. cachePolicy: NSURLRequestUseProtocolCachePolicy
  375. timeoutInterval: 30.0];
  376. return nullptr;
  377. }
  378. return getRequestForURL (url, headers, postData);
  379. };
  380. if (NSMutableURLRequest* request = getRequest())
  381. [[webView.get() mainFrame] loadRequest: request];
  382. }
  383. void goBack() override { [webView.get() goBack]; }
  384. void goForward() override { [webView.get() goForward]; }
  385. void stop() override { [webView.get() stopLoading: nil]; }
  386. void refresh() override { [webView.get() reload: nil]; }
  387. id getWebView() override { return webView.get(); }
  388. void mouseMove (const MouseEvent&)
  389. {
  390. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  391. // WebKit doesn't capture mouse-moves itself, so it seems the only way to make
  392. // them work is to push them via this non-public method..
  393. if ([webView.get() respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
  394. [webView.get() performSelector: @selector (_updateMouseoverWithFakeEvent)];
  395. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  396. }
  397. private:
  398. ObjCObjectHandle<WebView*> webView;
  399. ObjCObjectHandle<id> clickListener;
  400. };
  401. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  402. #endif
  403. class API_AVAILABLE (macos (10.11)) WKWebViewImpl : public WebViewBase
  404. {
  405. public:
  406. WKWebViewImpl (WebBrowserComponent* owner, const String& userAgent)
  407. {
  408. #if JUCE_MAC
  409. static WebViewKeyEquivalentResponder<WKWebView> webviewClass;
  410. webView.reset ([webviewClass.createInstance() initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)]);
  411. #else
  412. webView.reset ([[WKWebView alloc] initWithFrame: CGRectMake (0, 0, 100.0f, 100.0f)]);
  413. #endif
  414. if (userAgent.isNotEmpty())
  415. webView.get().customUserAgent = juceStringToNS (userAgent);
  416. static WebViewDelegateClass cls;
  417. webViewDelegate.reset ([cls.createInstance() init]);
  418. WebViewDelegateClass::setOwner (webViewDelegate.get(), owner);
  419. [webView.get() setNavigationDelegate: webViewDelegate.get()];
  420. [webView.get() setUIDelegate: webViewDelegate.get()];
  421. #if JUCE_DEBUG
  422. [[[webView.get() configuration] preferences] setValue: @(true) forKey: @"developerExtrasEnabled"];
  423. #endif
  424. }
  425. ~WKWebViewImpl() override
  426. {
  427. [webView.get() setNavigationDelegate: nil];
  428. [webView.get() setUIDelegate: nil];
  429. }
  430. void goToURL (const String& url,
  431. const StringArray* headers,
  432. const MemoryBlock* postData) override
  433. {
  434. auto trimmed = url.trimStart();
  435. if (trimmed.startsWithIgnoreCase ("javascript:"))
  436. {
  437. [webView.get() evaluateJavaScript: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))
  438. completionHandler: nil];
  439. return;
  440. }
  441. stop();
  442. if (trimmed.startsWithIgnoreCase ("file:"))
  443. {
  444. auto file = URL (url).getLocalFile();
  445. if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())])
  446. [webView.get() loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: nsUrl];
  447. }
  448. else if (NSMutableURLRequest* request = getRequestForURL (url, headers, postData))
  449. {
  450. [webView.get() loadRequest: request];
  451. }
  452. }
  453. void goBack() override { [webView.get() goBack]; }
  454. void goForward() override { [webView.get() goForward]; }
  455. void stop() override { [webView.get() stopLoading]; }
  456. void refresh() override { [webView.get() reload]; }
  457. id getWebView() override { return webView.get(); }
  458. private:
  459. ObjCObjectHandle<WKWebView*> webView;
  460. ObjCObjectHandle<id> webViewDelegate;
  461. };
  462. //==============================================================================
  463. class WebBrowserComponent::Pimpl
  464. #if JUCE_MAC
  465. : public NSViewComponent
  466. #else
  467. : public UIViewComponent
  468. #endif
  469. {
  470. public:
  471. Pimpl (WebBrowserComponent* owner, const String& userAgent)
  472. {
  473. if (@available (macOS 10.11, *))
  474. webView = std::make_unique<WKWebViewImpl> (owner, userAgent);
  475. #if JUCE_MAC
  476. else
  477. webView = std::make_unique<WebViewImpl> (owner, userAgent);
  478. #endif
  479. setView (webView->getWebView());
  480. }
  481. ~Pimpl() override
  482. {
  483. webView = nullptr;
  484. setView (nil);
  485. }
  486. void goToURL (const String& url,
  487. const StringArray* headers,
  488. const MemoryBlock* postData)
  489. {
  490. webView->goToURL (url, headers, postData);
  491. }
  492. void goBack() { webView->goBack(); }
  493. void goForward() { webView->goForward(); }
  494. void stop() { webView->stop(); }
  495. void refresh() { webView->refresh(); }
  496. private:
  497. std::unique_ptr<WebViewBase> webView;
  498. };
  499. //==============================================================================
  500. WebBrowserComponent::WebBrowserComponent (const Options& options)
  501. : unloadPageWhenHidden (! options.keepsPageLoadedWhenBrowserIsHidden())
  502. {
  503. setOpaque (true);
  504. browser.reset (new Pimpl (this, options.getUserAgent()));
  505. addAndMakeVisible (browser.get());
  506. }
  507. WebBrowserComponent::~WebBrowserComponent() = default;
  508. //==============================================================================
  509. void WebBrowserComponent::goToURL (const String& url,
  510. const StringArray* headers,
  511. const MemoryBlock* postData)
  512. {
  513. lastURL = url;
  514. if (headers != nullptr)
  515. lastHeaders = *headers;
  516. else
  517. lastHeaders.clear();
  518. if (postData != nullptr)
  519. lastPostData = *postData;
  520. else
  521. lastPostData.reset();
  522. blankPageShown = false;
  523. browser->goToURL (url, headers, postData);
  524. }
  525. void WebBrowserComponent::stop()
  526. {
  527. browser->stop();
  528. }
  529. void WebBrowserComponent::goBack()
  530. {
  531. lastURL.clear();
  532. blankPageShown = false;
  533. browser->goBack();
  534. }
  535. void WebBrowserComponent::goForward()
  536. {
  537. lastURL.clear();
  538. browser->goForward();
  539. }
  540. void WebBrowserComponent::refresh()
  541. {
  542. browser->refresh();
  543. }
  544. //==============================================================================
  545. void WebBrowserComponent::paint (Graphics&)
  546. {
  547. }
  548. void WebBrowserComponent::checkWindowAssociation()
  549. {
  550. if (isShowing())
  551. {
  552. reloadLastURL();
  553. if (blankPageShown)
  554. goBack();
  555. }
  556. else
  557. {
  558. if (unloadPageWhenHidden && ! blankPageShown)
  559. {
  560. // when the component becomes invisible, some stuff like flash
  561. // carries on playing audio, so we need to force it onto a blank
  562. // page to avoid this, (and send it back when it's made visible again).
  563. blankPageShown = true;
  564. browser->goToURL ("about:blank", nullptr, nullptr);
  565. }
  566. }
  567. }
  568. void WebBrowserComponent::reloadLastURL()
  569. {
  570. if (lastURL.isNotEmpty())
  571. {
  572. goToURL (lastURL, &lastHeaders, &lastPostData);
  573. lastURL.clear();
  574. }
  575. }
  576. void WebBrowserComponent::parentHierarchyChanged()
  577. {
  578. checkWindowAssociation();
  579. }
  580. void WebBrowserComponent::resized()
  581. {
  582. browser->setSize (getWidth(), getHeight());
  583. }
  584. void WebBrowserComponent::visibilityChanged()
  585. {
  586. checkWindowAssociation();
  587. }
  588. void WebBrowserComponent::focusGainedWithDirection (FocusChangeType, FocusChangeDirection)
  589. {
  590. }
  591. void WebBrowserComponent::clearCookies()
  592. {
  593. NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  594. if (NSArray* cookies = [storage cookies])
  595. {
  596. const NSUInteger n = [cookies count];
  597. for (NSUInteger i = 0; i < n; ++i)
  598. [storage deleteCookie: [cookies objectAtIndex: i]];
  599. }
  600. [[NSUserDefaults standardUserDefaults] synchronize];
  601. }
  602. //==============================================================================
  603. bool WebBrowserComponent::areOptionsSupported (const Options& options)
  604. {
  605. return (options.getBackend() == Options::Backend::defaultBackend);
  606. }
  607. } // namespace juce