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.

808 lines
28KB

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