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.

355 lines
11KB

  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. registerClass();
  28. }
  29. static void setOwner (id self, WebBrowserComponent* owner) { object_setInstanceVariable (self, "owner", owner); }
  30. static WebBrowserComponent* getOwner (id self) { return getIvar<WebBrowserComponent*> (self, "owner"); }
  31. private:
  32. static void decidePolicyForNavigationAction (id self, SEL, WebView*, NSDictionary* actionInformation,
  33. NSURLRequest*, WebFrame*, id <WebPolicyDecisionListener> listener)
  34. {
  35. NSURL* url = [actionInformation valueForKey: nsStringLiteral ("WebActionOriginalURLKey")];
  36. if (getOwner (self)->pageAboutToLoad (nsStringToJuce ([url absoluteString])))
  37. [listener use];
  38. else
  39. [listener ignore];
  40. }
  41. static void didFinishLoadForFrame (id self, SEL, WebView* sender, WebFrame* frame)
  42. {
  43. if ([frame isEqual: [sender mainFrame]])
  44. {
  45. NSURL* url = [[[frame dataSource] request] URL];
  46. getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString]));
  47. }
  48. }
  49. static void willCloseFrame (id self, SEL, WebView*, WebFrame*)
  50. {
  51. getOwner (self)->windowCloseRequest();
  52. }
  53. };
  54. #else
  55. } // (juce namespace)
  56. //==============================================================================
  57. @interface WebViewTapDetector : NSObject <UIGestureRecognizerDelegate>
  58. {
  59. }
  60. - (BOOL) gestureRecognizer: (UIGestureRecognizer*) gestureRecognizer
  61. shouldRecognizeSimultaneouslyWithGestureRecognizer: (UIGestureRecognizer*) otherGestureRecognizer;
  62. @end
  63. @implementation WebViewTapDetector
  64. - (BOOL) gestureRecognizer: (UIGestureRecognizer*) gestureRecognizer
  65. shouldRecognizeSimultaneouslyWithGestureRecognizer: (UIGestureRecognizer*) otherGestureRecognizer
  66. {
  67. (void) gestureRecognizer;
  68. (void) otherGestureRecognizer;
  69. return YES;
  70. }
  71. @end
  72. //==============================================================================
  73. @interface WebViewURLChangeDetector : NSObject <UIWebViewDelegate>
  74. {
  75. juce::WebBrowserComponent* ownerComponent;
  76. }
  77. - (WebViewURLChangeDetector*) initWithWebBrowserOwner: (juce::WebBrowserComponent*) ownerComponent;
  78. - (BOOL) webView: (UIWebView*) webView shouldStartLoadWithRequest: (NSURLRequest*) request navigationType: (UIWebViewNavigationType) navigationType;
  79. @end
  80. @implementation WebViewURLChangeDetector
  81. - (WebViewURLChangeDetector*) initWithWebBrowserOwner: (juce::WebBrowserComponent*) ownerComponent_
  82. {
  83. [super init];
  84. ownerComponent = ownerComponent_;
  85. return self;
  86. }
  87. - (BOOL) webView: (UIWebView*) webView shouldStartLoadWithRequest: (NSURLRequest*) request navigationType: (UIWebViewNavigationType) navigationType
  88. {
  89. (void) webView;
  90. (void) navigationType;
  91. return ownerComponent->pageAboutToLoad (nsStringToJuce (request.URL.absoluteString));
  92. }
  93. @end
  94. namespace juce {
  95. #endif
  96. //==============================================================================
  97. class WebBrowserComponent::Pimpl
  98. #if JUCE_MAC
  99. : public NSViewComponent
  100. #else
  101. : public UIViewComponent
  102. #endif
  103. {
  104. public:
  105. Pimpl (WebBrowserComponent* owner)
  106. {
  107. #if JUCE_MAC
  108. webView = [[WebView alloc] initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
  109. frameName: nsEmptyString()
  110. groupName: nsEmptyString()];
  111. setView (webView);
  112. static DownloadClickDetectorClass cls;
  113. clickListener = [cls.createInstance() init];
  114. DownloadClickDetectorClass::setOwner (clickListener, owner);
  115. [webView setPolicyDelegate: clickListener];
  116. [webView setFrameLoadDelegate: clickListener];
  117. #else
  118. webView = [[UIWebView alloc] initWithFrame: CGRectMake (0, 0, 1.0f, 1.0f)];
  119. setView (webView);
  120. tapDetector = [[WebViewTapDetector alloc] init];
  121. urlDetector = [[WebViewURLChangeDetector alloc] initWithWebBrowserOwner: owner];
  122. gestureRecogniser = nil;
  123. webView.delegate = urlDetector;
  124. #endif
  125. }
  126. ~Pimpl()
  127. {
  128. #if JUCE_MAC
  129. [webView setPolicyDelegate: nil];
  130. [webView setFrameLoadDelegate: nil];
  131. [clickListener release];
  132. #else
  133. webView.delegate = nil;
  134. [webView removeGestureRecognizer: gestureRecogniser];
  135. [gestureRecogniser release];
  136. [tapDetector release];
  137. [urlDetector release];
  138. #endif
  139. setView (nil);
  140. }
  141. void goToURL (const String& url,
  142. const StringArray* headers,
  143. const MemoryBlock* postData)
  144. {
  145. NSMutableURLRequest* r
  146. = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (url)]
  147. cachePolicy: NSURLRequestUseProtocolCachePolicy
  148. timeoutInterval: 30.0];
  149. if (postData != nullptr && postData->getSize() > 0)
  150. {
  151. [r setHTTPMethod: nsStringLiteral ("POST")];
  152. [r setHTTPBody: [NSData dataWithBytes: postData->getData()
  153. length: postData->getSize()]];
  154. }
  155. if (headers != nullptr)
  156. {
  157. for (int i = 0; i < headers->size(); ++i)
  158. {
  159. const String headerName ((*headers)[i].upToFirstOccurrenceOf (":", false, false).trim());
  160. const String headerValue ((*headers)[i].fromFirstOccurrenceOf (":", false, false).trim());
  161. [r setValue: juceStringToNS (headerValue)
  162. forHTTPHeaderField: juceStringToNS (headerName)];
  163. }
  164. }
  165. stop();
  166. #if JUCE_MAC
  167. [[webView mainFrame] loadRequest: r];
  168. #else
  169. [webView loadRequest: r];
  170. #endif
  171. }
  172. void goBack() { [webView goBack]; }
  173. void goForward() { [webView goForward]; }
  174. #if JUCE_MAC
  175. void stop() { [webView stopLoading: nil]; }
  176. void refresh() { [webView reload: nil]; }
  177. #else
  178. void stop() { [webView stopLoading]; }
  179. void refresh() { [webView reload]; }
  180. #endif
  181. void mouseMove (const MouseEvent&)
  182. {
  183. // WebKit doesn't capture mouse-moves itself, so it seems the only way to make
  184. // them work is to push them via this non-public method..
  185. if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
  186. [webView performSelector: @selector (_updateMouseoverWithFakeEvent)];
  187. }
  188. private:
  189. #if JUCE_MAC
  190. WebView* webView;
  191. NSObject* clickListener;
  192. #else
  193. UIWebView* webView;
  194. WebViewTapDetector* tapDetector;
  195. WebViewURLChangeDetector* urlDetector;
  196. UITapGestureRecognizer* gestureRecogniser;
  197. #endif
  198. };
  199. //==============================================================================
  200. WebBrowserComponent::WebBrowserComponent (const bool unloadWhenHidden)
  201. : browser (nullptr),
  202. blankPageShown (false),
  203. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  204. {
  205. setOpaque (true);
  206. addAndMakeVisible (browser = new Pimpl (this));
  207. }
  208. WebBrowserComponent::~WebBrowserComponent()
  209. {
  210. deleteAndZero (browser);
  211. }
  212. //==============================================================================
  213. void WebBrowserComponent::goToURL (const String& url,
  214. const StringArray* headers,
  215. const MemoryBlock* postData)
  216. {
  217. lastURL = url;
  218. lastHeaders.clear();
  219. if (headers != nullptr)
  220. lastHeaders = *headers;
  221. lastPostData.setSize (0);
  222. if (postData != nullptr)
  223. lastPostData = *postData;
  224. blankPageShown = false;
  225. browser->goToURL (url, headers, postData);
  226. }
  227. void WebBrowserComponent::stop()
  228. {
  229. browser->stop();
  230. }
  231. void WebBrowserComponent::goBack()
  232. {
  233. lastURL.clear();
  234. blankPageShown = false;
  235. browser->goBack();
  236. }
  237. void WebBrowserComponent::goForward()
  238. {
  239. lastURL.clear();
  240. browser->goForward();
  241. }
  242. void WebBrowserComponent::refresh()
  243. {
  244. browser->refresh();
  245. }
  246. //==============================================================================
  247. void WebBrowserComponent::paint (Graphics&)
  248. {
  249. }
  250. void WebBrowserComponent::checkWindowAssociation()
  251. {
  252. if (isShowing())
  253. {
  254. reloadLastURL();
  255. if (blankPageShown)
  256. goBack();
  257. }
  258. else
  259. {
  260. if (unloadPageWhenBrowserIsHidden && ! blankPageShown)
  261. {
  262. // when the component becomes invisible, some stuff like flash
  263. // carries on playing audio, so we need to force it onto a blank
  264. // page to avoid this, (and send it back when it's made visible again).
  265. blankPageShown = true;
  266. browser->goToURL ("about:blank", 0, 0);
  267. }
  268. }
  269. }
  270. void WebBrowserComponent::reloadLastURL()
  271. {
  272. if (lastURL.isNotEmpty())
  273. {
  274. goToURL (lastURL, &lastHeaders, &lastPostData);
  275. lastURL.clear();
  276. }
  277. }
  278. void WebBrowserComponent::parentHierarchyChanged()
  279. {
  280. checkWindowAssociation();
  281. }
  282. void WebBrowserComponent::resized()
  283. {
  284. browser->setSize (getWidth(), getHeight());
  285. }
  286. void WebBrowserComponent::visibilityChanged()
  287. {
  288. checkWindowAssociation();
  289. }