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
27KB

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