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 11KB

10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. if (headers != nullptr)
  219. lastHeaders = *headers;
  220. else
  221. lastHeaders.clear();
  222. if (postData != nullptr)
  223. lastPostData = *postData;
  224. else
  225. lastPostData.reset();
  226. blankPageShown = false;
  227. browser->goToURL (url, headers, postData);
  228. }
  229. void WebBrowserComponent::stop()
  230. {
  231. browser->stop();
  232. }
  233. void WebBrowserComponent::goBack()
  234. {
  235. lastURL.clear();
  236. blankPageShown = false;
  237. browser->goBack();
  238. }
  239. void WebBrowserComponent::goForward()
  240. {
  241. lastURL.clear();
  242. browser->goForward();
  243. }
  244. void WebBrowserComponent::refresh()
  245. {
  246. browser->refresh();
  247. }
  248. //==============================================================================
  249. void WebBrowserComponent::paint (Graphics&)
  250. {
  251. }
  252. void WebBrowserComponent::checkWindowAssociation()
  253. {
  254. if (isShowing())
  255. {
  256. reloadLastURL();
  257. if (blankPageShown)
  258. goBack();
  259. }
  260. else
  261. {
  262. if (unloadPageWhenBrowserIsHidden && ! blankPageShown)
  263. {
  264. // when the component becomes invisible, some stuff like flash
  265. // carries on playing audio, so we need to force it onto a blank
  266. // page to avoid this, (and send it back when it's made visible again).
  267. blankPageShown = true;
  268. browser->goToURL ("about:blank", 0, 0);
  269. }
  270. }
  271. }
  272. void WebBrowserComponent::reloadLastURL()
  273. {
  274. if (lastURL.isNotEmpty())
  275. {
  276. goToURL (lastURL, &lastHeaders, &lastPostData);
  277. lastURL.clear();
  278. }
  279. }
  280. void WebBrowserComponent::parentHierarchyChanged()
  281. {
  282. checkWindowAssociation();
  283. }
  284. void WebBrowserComponent::resized()
  285. {
  286. browser->setSize (getWidth(), getHeight());
  287. }
  288. void WebBrowserComponent::visibilityChanged()
  289. {
  290. checkWindowAssociation();
  291. }