Audio plugin host https://kx.studio/carla
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.

743 lines
27KB

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