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.

juce_mac_WebBrowserComponent.mm 12KB

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