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.

767 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 retain];
  196. }
  197. ~DeletedFileChooserWrapper()
  198. {
  199. [listener release];
  200. }
  201. std::unique_ptr<FileChooser> chooser;
  202. id<WebOpenPanelResultListener> listener;
  203. };
  204. auto chooser = std::make_unique<FileChooser> (TRANS("Select the file you want to upload..."),
  205. File::getSpecialLocation (File::userHomeDirectory), "*");
  206. auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), resultListener);
  207. auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
  208. | (allowMultipleFiles ? FileBrowserComponent::canSelectMultipleItems : 0);
  209. wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
  210. {
  211. for (auto& f : wrapper->chooser->getResults())
  212. [wrapper->listener chooseFilename: juceStringToNS (f.getFullPathName())];
  213. delete wrapper;
  214. });
  215. }
  216. };
  217. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  218. #endif
  219. struct WebViewDelegateClass : public ObjCClass<NSObject>
  220. {
  221. WebViewDelegateClass() : ObjCClass<NSObject> ("JUCEWebViewDelegate_")
  222. {
  223. addIvar<WebBrowserComponent*> ("owner");
  224. addMethod (@selector (webView:decidePolicyForNavigationAction:decisionHandler:), decidePolicyForNavigationAction, "v@:@@@");
  225. addMethod (@selector (webView:didFinishNavigation:), didFinishNavigation, "v@:@@");
  226. addMethod (@selector (webView:didFailNavigation:withError:), didFailNavigation, "v@:@@@");
  227. addMethod (@selector (webView:didFailProvisionalNavigation:withError:), didFailProvisionalNavigation, "v@:@@@");
  228. addMethod (@selector (webViewDidClose:), webViewDidClose, "v@:@");
  229. addMethod (@selector (webView:createWebViewWithConfiguration:forNavigationAction:
  230. windowFeatures:), createWebView, "@@:@@@@");
  231. #if WKWEBVIEW_OPENPANEL_SUPPORTED
  232. if (@available (macOS 10.12, *))
  233. addMethod (@selector (webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:), runOpenPanel, "v@:@@@@");
  234. #endif
  235. registerClass();
  236. }
  237. static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); }
  238. static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); }
  239. private:
  240. static void decidePolicyForNavigationAction (id self, SEL, WKWebView*, WKNavigationAction* navigationAction,
  241. void (^decisionHandler)(WKNavigationActionPolicy))
  242. {
  243. if (getOwner (self)->pageAboutToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString])))
  244. decisionHandler (WKNavigationActionPolicyAllow);
  245. else
  246. decisionHandler (WKNavigationActionPolicyCancel);
  247. }
  248. static void didFinishNavigation (id self, SEL, WKWebView* webview, WKNavigation*)
  249. {
  250. getOwner (self)->pageFinishedLoading (nsStringToJuce ([[webview URL] absoluteString]));
  251. }
  252. static void displayError (WebBrowserComponent* owner, NSError* error)
  253. {
  254. if ([error code] != NSURLErrorCancelled)
  255. {
  256. auto errorString = nsStringToJuce ([error localizedDescription]);
  257. bool proceedToErrorPage = owner->pageLoadHadNetworkError (errorString);
  258. // WKWebView doesn't have an internal error page, so make a really simple one ourselves
  259. if (proceedToErrorPage)
  260. owner->goToURL ("data:text/plain;charset=UTF-8," + errorString);
  261. }
  262. }
  263. static void didFailNavigation (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
  264. {
  265. displayError (getOwner (self), error);
  266. }
  267. static void didFailProvisionalNavigation (id self, SEL, WKWebView*, WKNavigation*, NSError* error)
  268. {
  269. displayError (getOwner (self), error);
  270. }
  271. static void webViewDidClose (id self, SEL, WKWebView*)
  272. {
  273. getOwner (self)->windowCloseRequest();
  274. }
  275. static WKWebView* createWebView (id self, SEL, WKWebView*, WKWebViewConfiguration*,
  276. WKNavigationAction* navigationAction, WKWindowFeatures*)
  277. {
  278. getOwner (self)->newWindowAttemptingToLoad (nsStringToJuce ([[[navigationAction request] URL] absoluteString]));
  279. return nil;
  280. }
  281. #if WKWEBVIEW_OPENPANEL_SUPPORTED
  282. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new")
  283. static void runOpenPanel (id, SEL, WKWebView*, WKOpenPanelParameters* parameters, WKFrameInfo*,
  284. void (^completionHandler)(NSArray<NSURL*>*))
  285. {
  286. using CompletionHandlerType = decltype (completionHandler);
  287. class DeletedFileChooserWrapper : private DeletedAtShutdown
  288. {
  289. public:
  290. DeletedFileChooserWrapper (std::unique_ptr<FileChooser> fc, CompletionHandlerType h)
  291. : chooser (std::move (fc)), handler (h)
  292. {
  293. [handler retain];
  294. }
  295. ~DeletedFileChooserWrapper()
  296. {
  297. callHandler (nullptr);
  298. [handler release];
  299. }
  300. void callHandler (NSArray<NSURL*>* urls)
  301. {
  302. if (handlerCalled)
  303. return;
  304. handler (urls);
  305. handlerCalled = true;
  306. }
  307. std::unique_ptr<FileChooser> chooser;
  308. private:
  309. CompletionHandlerType handler;
  310. bool handlerCalled = false;
  311. };
  312. auto chooser = std::make_unique<FileChooser> (TRANS("Select the file you want to upload..."),
  313. File::getSpecialLocation (File::userHomeDirectory), "*");
  314. auto* wrapper = new DeletedFileChooserWrapper (std::move (chooser), completionHandler);
  315. auto flags = FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles
  316. | ([parameters allowsMultipleSelection] ? FileBrowserComponent::canSelectMultipleItems : 0);
  317. #if (defined (MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14)
  318. if (@available (macOS 10.14, *))
  319. {
  320. if ([parameters allowsDirectories])
  321. flags |= FileBrowserComponent::canSelectDirectories;
  322. }
  323. #endif
  324. wrapper->chooser->launchAsync (flags, [wrapper] (const FileChooser&)
  325. {
  326. auto results = wrapper->chooser->getResults();
  327. auto urls = [NSMutableArray arrayWithCapacity: (NSUInteger) results.size()];
  328. for (auto& f : results)
  329. [urls addObject: [NSURL fileURLWithPath: juceStringToNS (f.getFullPathName())]];
  330. wrapper->callHandler (urls);
  331. delete wrapper;
  332. });
  333. }
  334. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  335. #endif
  336. };
  337. //==============================================================================
  338. struct WebViewBase
  339. {
  340. virtual ~WebViewBase() = default;
  341. virtual void goToURL (const String&, const StringArray*, const MemoryBlock*) = 0;
  342. virtual void goBack() = 0;
  343. virtual void goForward() = 0;
  344. virtual void stop() = 0;
  345. virtual void refresh() = 0;
  346. virtual id getWebView() = 0;
  347. };
  348. #if JUCE_MAC
  349. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
  350. class WebViewImpl : public WebViewBase
  351. {
  352. public:
  353. WebViewImpl (WebBrowserComponent* owner)
  354. {
  355. static WebViewKeyEquivalentResponder<WebView> webviewClass;
  356. webView = (WebView*) webviewClass.createInstance();
  357. webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
  358. frameName: nsEmptyString()
  359. groupName: nsEmptyString()];
  360. static DownloadClickDetectorClass cls;
  361. clickListener = [cls.createInstance() init];
  362. DownloadClickDetectorClass::setOwner (clickListener, owner);
  363. [webView setPolicyDelegate: clickListener];
  364. [webView setFrameLoadDelegate: clickListener];
  365. [webView setUIDelegate: clickListener];
  366. }
  367. ~WebViewImpl() override
  368. {
  369. [webView setPolicyDelegate: nil];
  370. [webView setFrameLoadDelegate: nil];
  371. [webView setUIDelegate: nil];
  372. [clickListener release];
  373. }
  374. void goToURL (const String& url,
  375. const StringArray* headers,
  376. const MemoryBlock* postData) override
  377. {
  378. if (url.trimStart().startsWithIgnoreCase ("javascript:"))
  379. {
  380. [webView stringByEvaluatingJavaScriptFromString: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))];
  381. return;
  382. }
  383. stop();
  384. auto getRequest = [&]() -> NSMutableURLRequest*
  385. {
  386. if (url.trimStart().startsWithIgnoreCase ("file:"))
  387. {
  388. auto file = URL (url).getLocalFile();
  389. if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())])
  390. return [NSMutableURLRequest requestWithURL: appendParametersToFileURL (url, nsUrl)
  391. cachePolicy: NSURLRequestUseProtocolCachePolicy
  392. timeoutInterval: 30.0];
  393. return nullptr;
  394. }
  395. return getRequestForURL (url, headers, postData);
  396. };
  397. if (NSMutableURLRequest* request = getRequest())
  398. [[webView mainFrame] loadRequest: request];
  399. }
  400. void goBack() override { [webView goBack]; }
  401. void goForward() override { [webView goForward]; }
  402. void stop() override { [webView stopLoading: nil]; }
  403. void refresh() override { [webView reload: nil]; }
  404. id getWebView() override { return webView; }
  405. void mouseMove (const MouseEvent&)
  406. {
  407. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
  408. // WebKit doesn't capture mouse-moves itself, so it seems the only way to make
  409. // them work is to push them via this non-public method..
  410. if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
  411. [webView performSelector: @selector (_updateMouseoverWithFakeEvent)];
  412. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  413. }
  414. private:
  415. WebView* webView = nil;
  416. id clickListener;
  417. };
  418. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  419. #endif
  420. class WKWebViewImpl : public WebViewBase
  421. {
  422. public:
  423. WKWebViewImpl (WebBrowserComponent* owner)
  424. {
  425. #if JUCE_MAC
  426. static WebViewKeyEquivalentResponder<WKWebView> webviewClass;
  427. webView = (WKWebView*) webviewClass.createInstance();
  428. webView = [webView initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)];
  429. #else
  430. webView = [[WKWebView alloc] initWithFrame: CGRectMake (0, 0, 100.0f, 100.0f)];
  431. #endif
  432. static WebViewDelegateClass cls;
  433. webViewDelegate = [cls.createInstance() init];
  434. WebViewDelegateClass::setOwner (webViewDelegate, owner);
  435. [webView setNavigationDelegate: webViewDelegate];
  436. [webView setUIDelegate: webViewDelegate];
  437. }
  438. ~WKWebViewImpl() override
  439. {
  440. [webView setNavigationDelegate: nil];
  441. [webView setUIDelegate: nil];
  442. [webViewDelegate release];
  443. }
  444. void goToURL (const String& url,
  445. const StringArray* headers,
  446. const MemoryBlock* postData) override
  447. {
  448. auto trimmed = url.trimStart();
  449. if (trimmed.startsWithIgnoreCase ("javascript:"))
  450. {
  451. [webView evaluateJavaScript: juceStringToNS (url.fromFirstOccurrenceOf (":", false, false))
  452. completionHandler: nil];
  453. return;
  454. }
  455. stop();
  456. if (trimmed.startsWithIgnoreCase ("file:"))
  457. {
  458. auto file = URL (url).getLocalFile();
  459. if (NSURL* nsUrl = [NSURL fileURLWithPath: juceStringToNS (file.getFullPathName())])
  460. [webView loadFileURL: appendParametersToFileURL (url, nsUrl) allowingReadAccessToURL: nsUrl];
  461. }
  462. else if (NSMutableURLRequest* request = getRequestForURL (url, headers, postData))
  463. {
  464. [webView loadRequest: request];
  465. }
  466. }
  467. void goBack() override { [webView goBack]; }
  468. void goForward() override { [webView goForward]; }
  469. void stop() override { [webView stopLoading]; }
  470. void refresh() override { [webView reload]; }
  471. id getWebView() override { return webView; }
  472. private:
  473. WKWebView* webView = nil;
  474. id webViewDelegate;
  475. };
  476. //==============================================================================
  477. class WebBrowserComponent::Pimpl
  478. #if JUCE_MAC
  479. : public NSViewComponent
  480. #else
  481. : public UIViewComponent
  482. #endif
  483. {
  484. public:
  485. Pimpl (WebBrowserComponent* owner)
  486. {
  487. if (@available (macOS 10.10, *))
  488. webView = std::make_unique<WKWebViewImpl> (owner);
  489. #if JUCE_MAC
  490. else
  491. webView = std::make_unique<WebViewImpl> (owner);
  492. #endif
  493. setView (webView->getWebView());
  494. }
  495. ~Pimpl()
  496. {
  497. webView = nullptr;
  498. setView (nil);
  499. }
  500. void goToURL (const String& url,
  501. const StringArray* headers,
  502. const MemoryBlock* postData)
  503. {
  504. webView->goToURL (url, headers, postData);
  505. }
  506. void goBack() { webView->goBack(); }
  507. void goForward() { webView->goForward(); }
  508. void stop() { webView->stop(); }
  509. void refresh() { webView->refresh(); }
  510. private:
  511. std::unique_ptr<WebViewBase> webView;
  512. };
  513. //==============================================================================
  514. WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden)
  515. : unloadPageWhenHidden (unloadWhenHidden)
  516. {
  517. setOpaque (true);
  518. browser.reset (new Pimpl (this));
  519. addAndMakeVisible (browser.get());
  520. }
  521. WebBrowserComponent::~WebBrowserComponent()
  522. {
  523. }
  524. //==============================================================================
  525. void WebBrowserComponent::goToURL (const String& url,
  526. const StringArray* headers,
  527. const MemoryBlock* postData)
  528. {
  529. lastURL = url;
  530. if (headers != nullptr)
  531. lastHeaders = *headers;
  532. else
  533. lastHeaders.clear();
  534. if (postData != nullptr)
  535. lastPostData = *postData;
  536. else
  537. lastPostData.reset();
  538. blankPageShown = false;
  539. browser->goToURL (url, headers, postData);
  540. }
  541. void WebBrowserComponent::stop()
  542. {
  543. browser->stop();
  544. }
  545. void WebBrowserComponent::goBack()
  546. {
  547. lastURL.clear();
  548. blankPageShown = false;
  549. browser->goBack();
  550. }
  551. void WebBrowserComponent::goForward()
  552. {
  553. lastURL.clear();
  554. browser->goForward();
  555. }
  556. void WebBrowserComponent::refresh()
  557. {
  558. browser->refresh();
  559. }
  560. //==============================================================================
  561. void WebBrowserComponent::paint (Graphics&)
  562. {
  563. }
  564. void WebBrowserComponent::checkWindowAssociation()
  565. {
  566. if (isShowing())
  567. {
  568. reloadLastURL();
  569. if (blankPageShown)
  570. goBack();
  571. }
  572. else
  573. {
  574. if (unloadPageWhenHidden && ! blankPageShown)
  575. {
  576. // when the component becomes invisible, some stuff like flash
  577. // carries on playing audio, so we need to force it onto a blank
  578. // page to avoid this, (and send it back when it's made visible again).
  579. blankPageShown = true;
  580. browser->goToURL ("about:blank", nullptr, nullptr);
  581. }
  582. }
  583. }
  584. void WebBrowserComponent::reloadLastURL()
  585. {
  586. if (lastURL.isNotEmpty())
  587. {
  588. goToURL (lastURL, &lastHeaders, &lastPostData);
  589. lastURL.clear();
  590. }
  591. }
  592. void WebBrowserComponent::parentHierarchyChanged()
  593. {
  594. checkWindowAssociation();
  595. }
  596. void WebBrowserComponent::resized()
  597. {
  598. browser->setSize (getWidth(), getHeight());
  599. }
  600. void WebBrowserComponent::visibilityChanged()
  601. {
  602. checkWindowAssociation();
  603. }
  604. void WebBrowserComponent::focusGained (FocusChangeType)
  605. {
  606. }
  607. void WebBrowserComponent::clearCookies()
  608. {
  609. NSHTTPCookieStorage* storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
  610. if (NSArray* cookies = [storage cookies])
  611. {
  612. const NSUInteger n = [cookies count];
  613. for (NSUInteger i = 0; i < n; ++i)
  614. [storage deleteCookie: [cookies objectAtIndex: i]];
  615. }
  616. [[NSUserDefaults standardUserDefaults] synchronize];
  617. }
  618. } // namespace juce