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.

380 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #if JUCE_MAC
  18. struct DownloadClickDetectorClass : public ObjCClass <NSObject>
  19. {
  20. DownloadClickDetectorClass() : ObjCClass <NSObject> ("JUCEWebClickDetector_")
  21. {
  22. addIvar <WebBrowserComponent*> ("owner");
  23. addMethod (@selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:),
  24. decidePolicyForNavigationAction, "v@:@@@@@");
  25. addMethod (@selector (webView:didFinishLoadForFrame:), didFinishLoadForFrame, "v@:@@");
  26. addMethod (@selector (webView:willCloseFrame:), willCloseFrame, "v@:@@");
  27. addMethod (@selector (webView:runOpenPanelForFileButtonWithResultListener:allowMultipleFiles:), runOpenPanel, "v@:@@", @encode (BOOL));
  28. registerClass();
  29. }
  30. static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); }
  31. static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); }
  32. private:
  33. static void decidePolicyForNavigationAction (id self, SEL, WebView*, NSDictionary* actionInformation,
  34. NSURLRequest*, WebFrame*, id <WebPolicyDecisionListener> listener)
  35. {
  36. NSURL* url = [actionInformation valueForKey: nsStringLiteral ("WebActionOriginalURLKey")];
  37. if (getOwner (self)->pageAboutToLoad (nsStringToJuce ([url absoluteString])))
  38. [listener use];
  39. else
  40. [listener ignore];
  41. }
  42. static void didFinishLoadForFrame (id self, SEL, WebView* sender, WebFrame* frame)
  43. {
  44. if ([frame isEqual: [sender mainFrame]])
  45. {
  46. NSURL* url = [[[frame dataSource] request] URL];
  47. getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString]));
  48. }
  49. }
  50. static void willCloseFrame (id self, SEL, WebView*, WebFrame*)
  51. {
  52. getOwner (self)->windowCloseRequest();
  53. }
  54. static void runOpenPanel (id, SEL, WebView*, id<WebOpenPanelResultListener> resultListener, BOOL allowMultipleFiles)
  55. {
  56. #if JUCE_MODAL_LOOPS_PERMITTED
  57. FileChooser chooser (TRANS("Select the file you want to upload..."),
  58. File::getSpecialLocation (File::userHomeDirectory), "*");
  59. if (allowMultipleFiles ? chooser.browseForMultipleFilesToOpen()
  60. : chooser.browseForFileToOpen())
  61. {
  62. const Array<File>& files = chooser.getResults();
  63. for (int i = 0; i < files.size(); ++i)
  64. [resultListener chooseFilename: juceStringToNS (files.getReference(i).getFullPathName())];
  65. }
  66. #else
  67. (void) resultListener; (void) allowMultipleFiles;
  68. jassertfalse; // Can't use this without modal loops being enabled!
  69. #endif
  70. }
  71. };
  72. #else
  73. } // (juce namespace)
  74. //==============================================================================
  75. @interface WebViewTapDetector : NSObject <UIGestureRecognizerDelegate>
  76. {
  77. }
  78. - (BOOL) gestureRecognizer: (UIGestureRecognizer*) gestureRecognizer
  79. shouldRecognizeSimultaneouslyWithGestureRecognizer: (UIGestureRecognizer*) otherGestureRecognizer;
  80. @end
  81. @implementation WebViewTapDetector
  82. - (BOOL) gestureRecognizer: (UIGestureRecognizer*) gestureRecognizer
  83. shouldRecognizeSimultaneouslyWithGestureRecognizer: (UIGestureRecognizer*) otherGestureRecognizer
  84. {
  85. (void) gestureRecognizer;
  86. (void) otherGestureRecognizer;
  87. return YES;
  88. }
  89. @end
  90. //==============================================================================
  91. @interface WebViewURLChangeDetector : NSObject <UIWebViewDelegate>
  92. {
  93. juce::WebBrowserComponent* ownerComponent;
  94. }
  95. - (WebViewURLChangeDetector*) initWithWebBrowserOwner: (juce::WebBrowserComponent*) ownerComponent;
  96. - (BOOL) webView: (UIWebView*) webView shouldStartLoadWithRequest: (NSURLRequest*) request navigationType: (UIWebViewNavigationType) navigationType;
  97. @end
  98. @implementation WebViewURLChangeDetector
  99. - (WebViewURLChangeDetector*) initWithWebBrowserOwner: (juce::WebBrowserComponent*) ownerComponent_
  100. {
  101. [super init];
  102. ownerComponent = ownerComponent_;
  103. return self;
  104. }
  105. - (BOOL) webView: (UIWebView*) webView shouldStartLoadWithRequest: (NSURLRequest*) request navigationType: (UIWebViewNavigationType) navigationType
  106. {
  107. (void) webView;
  108. (void) navigationType;
  109. return ownerComponent->pageAboutToLoad (nsStringToJuce (request.URL.absoluteString));
  110. }
  111. @end
  112. namespace juce {
  113. #endif
  114. //==============================================================================
  115. class WebBrowserComponent::Pimpl
  116. #if JUCE_MAC
  117. : public NSViewComponent
  118. #else
  119. : public UIViewComponent
  120. #endif
  121. {
  122. public:
  123. Pimpl (WebBrowserComponent* owner)
  124. {
  125. #if JUCE_MAC
  126. webView = [[WebView alloc] initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
  127. frameName: nsEmptyString()
  128. groupName: nsEmptyString()];
  129. setView (webView);
  130. static DownloadClickDetectorClass cls;
  131. clickListener = [cls.createInstance() init];
  132. DownloadClickDetectorClass::setOwner (clickListener, owner);
  133. [webView setPolicyDelegate: clickListener];
  134. [webView setFrameLoadDelegate: clickListener];
  135. [webView setUIDelegate: clickListener];
  136. #else
  137. webView = [[UIWebView alloc] initWithFrame: CGRectMake (0, 0, 1.0f, 1.0f)];
  138. setView (webView);
  139. tapDetector = [[WebViewTapDetector alloc] init];
  140. urlDetector = [[WebViewURLChangeDetector alloc] initWithWebBrowserOwner: owner];
  141. gestureRecogniser = nil;
  142. webView.delegate = urlDetector;
  143. #endif
  144. }
  145. ~Pimpl()
  146. {
  147. #if JUCE_MAC
  148. [webView setPolicyDelegate: nil];
  149. [webView setFrameLoadDelegate: nil];
  150. [webView setUIDelegate: nil];
  151. [clickListener release];
  152. #else
  153. webView.delegate = nil;
  154. [webView removeGestureRecognizer: gestureRecogniser];
  155. [gestureRecogniser release];
  156. [tapDetector release];
  157. [urlDetector release];
  158. #endif
  159. setView (nil);
  160. }
  161. void goToURL (const String& url,
  162. const StringArray* headers,
  163. const MemoryBlock* postData)
  164. {
  165. NSMutableURLRequest* r
  166. = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (url)]
  167. cachePolicy: NSURLRequestUseProtocolCachePolicy
  168. timeoutInterval: 30.0];
  169. if (postData != nullptr && postData->getSize() > 0)
  170. {
  171. [r setHTTPMethod: nsStringLiteral ("POST")];
  172. [r setHTTPBody: [NSData dataWithBytes: postData->getData()
  173. length: postData->getSize()]];
  174. }
  175. if (headers != nullptr)
  176. {
  177. for (int i = 0; i < headers->size(); ++i)
  178. {
  179. const String headerName ((*headers)[i].upToFirstOccurrenceOf (":", false, false).trim());
  180. const String headerValue ((*headers)[i].fromFirstOccurrenceOf (":", false, false).trim());
  181. [r setValue: juceStringToNS (headerValue)
  182. forHTTPHeaderField: juceStringToNS (headerName)];
  183. }
  184. }
  185. stop();
  186. #if JUCE_MAC
  187. [[webView mainFrame] loadRequest: r];
  188. #else
  189. [webView loadRequest: r];
  190. #endif
  191. }
  192. void goBack() { [webView goBack]; }
  193. void goForward() { [webView goForward]; }
  194. #if JUCE_MAC
  195. void stop() { [webView stopLoading: nil]; }
  196. void refresh() { [webView reload: nil]; }
  197. #else
  198. void stop() { [webView stopLoading]; }
  199. void refresh() { [webView reload]; }
  200. #endif
  201. void mouseMove (const MouseEvent&)
  202. {
  203. // WebKit doesn't capture mouse-moves itself, so it seems the only way to make
  204. // them work is to push them via this non-public method..
  205. if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
  206. [webView performSelector: @selector (_updateMouseoverWithFakeEvent)];
  207. }
  208. private:
  209. #if JUCE_MAC
  210. WebView* webView;
  211. NSObject* clickListener;
  212. #else
  213. UIWebView* webView;
  214. WebViewTapDetector* tapDetector;
  215. WebViewURLChangeDetector* urlDetector;
  216. UITapGestureRecognizer* gestureRecogniser;
  217. #endif
  218. };
  219. //==============================================================================
  220. WebBrowserComponent::WebBrowserComponent (const bool unloadWhenHidden)
  221. : browser (nullptr),
  222. blankPageShown (false),
  223. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  224. {
  225. setOpaque (true);
  226. addAndMakeVisible (browser = new Pimpl (this));
  227. }
  228. WebBrowserComponent::~WebBrowserComponent()
  229. {
  230. deleteAndZero (browser);
  231. }
  232. //==============================================================================
  233. void WebBrowserComponent::goToURL (const String& url,
  234. const StringArray* headers,
  235. const MemoryBlock* postData)
  236. {
  237. lastURL = url;
  238. if (headers != nullptr)
  239. lastHeaders = *headers;
  240. else
  241. lastHeaders.clear();
  242. if (postData != nullptr)
  243. lastPostData = *postData;
  244. else
  245. lastPostData.reset();
  246. blankPageShown = false;
  247. browser->goToURL (url, headers, postData);
  248. }
  249. void WebBrowserComponent::stop()
  250. {
  251. browser->stop();
  252. }
  253. void WebBrowserComponent::goBack()
  254. {
  255. lastURL.clear();
  256. blankPageShown = false;
  257. browser->goBack();
  258. }
  259. void WebBrowserComponent::goForward()
  260. {
  261. lastURL.clear();
  262. browser->goForward();
  263. }
  264. void WebBrowserComponent::refresh()
  265. {
  266. browser->refresh();
  267. }
  268. //==============================================================================
  269. void WebBrowserComponent::paint (Graphics&)
  270. {
  271. }
  272. void WebBrowserComponent::checkWindowAssociation()
  273. {
  274. if (isShowing())
  275. {
  276. reloadLastURL();
  277. if (blankPageShown)
  278. goBack();
  279. }
  280. else
  281. {
  282. if (unloadPageWhenBrowserIsHidden && ! blankPageShown)
  283. {
  284. // when the component becomes invisible, some stuff like flash
  285. // carries on playing audio, so we need to force it onto a blank
  286. // page to avoid this, (and send it back when it's made visible again).
  287. blankPageShown = true;
  288. browser->goToURL ("about:blank", 0, 0);
  289. }
  290. }
  291. }
  292. void WebBrowserComponent::reloadLastURL()
  293. {
  294. if (lastURL.isNotEmpty())
  295. {
  296. goToURL (lastURL, &lastHeaders, &lastPostData);
  297. lastURL.clear();
  298. }
  299. }
  300. void WebBrowserComponent::parentHierarchyChanged()
  301. {
  302. checkWindowAssociation();
  303. }
  304. void WebBrowserComponent::resized()
  305. {
  306. browser->setSize (getWidth(), getHeight());
  307. }
  308. void WebBrowserComponent::visibilityChanged()
  309. {
  310. checkWindowAssociation();
  311. }