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.

347 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #if JUCE_MAC
  19. struct DownloadClickDetectorClass : public ObjCClass <NSObject>
  20. {
  21. DownloadClickDetectorClass() : ObjCClass <NSObject> ("JUCEWebClickDetector_")
  22. {
  23. addIvar <WebBrowserComponent*> ("owner");
  24. addMethod (@selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:),
  25. decidePolicyForNavigationAction, "v@:@@@@@");
  26. addMethod (@selector (webView:didFinishLoadForFrame:), didFinishLoadForFrame, "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. };
  50. #else
  51. } // (juce namespace)
  52. //==============================================================================
  53. @interface WebViewTapDetector : NSObject <UIGestureRecognizerDelegate>
  54. {
  55. }
  56. - (BOOL) gestureRecognizer: (UIGestureRecognizer*) gestureRecognizer
  57. shouldRecognizeSimultaneouslyWithGestureRecognizer: (UIGestureRecognizer*) otherGestureRecognizer;
  58. @end
  59. @implementation WebViewTapDetector
  60. - (BOOL) gestureRecognizer: (UIGestureRecognizer*) gestureRecognizer
  61. shouldRecognizeSimultaneouslyWithGestureRecognizer: (UIGestureRecognizer*) otherGestureRecognizer
  62. {
  63. return YES;
  64. }
  65. @end
  66. //==============================================================================
  67. @interface WebViewURLChangeDetector : NSObject <UIWebViewDelegate>
  68. {
  69. juce::WebBrowserComponent* ownerComponent;
  70. }
  71. - (WebViewURLChangeDetector*) initWithWebBrowserOwner: (juce::WebBrowserComponent*) ownerComponent;
  72. - (BOOL) webView: (UIWebView*) webView shouldStartLoadWithRequest: (NSURLRequest*) request navigationType: (UIWebViewNavigationType) navigationType;
  73. @end
  74. @implementation WebViewURLChangeDetector
  75. - (WebViewURLChangeDetector*) initWithWebBrowserOwner: (juce::WebBrowserComponent*) ownerComponent_
  76. {
  77. [super init];
  78. ownerComponent = ownerComponent_;
  79. return self;
  80. }
  81. - (BOOL) webView: (UIWebView*) webView shouldStartLoadWithRequest: (NSURLRequest*) request navigationType: (UIWebViewNavigationType) navigationType
  82. {
  83. return ownerComponent->pageAboutToLoad (nsStringToJuce (request.URL.absoluteString));
  84. }
  85. @end
  86. namespace juce {
  87. #endif
  88. //==============================================================================
  89. class WebBrowserComponent::Pimpl
  90. #if JUCE_MAC
  91. : public NSViewComponent
  92. #else
  93. : public UIViewComponent
  94. #endif
  95. {
  96. public:
  97. Pimpl (WebBrowserComponent* owner)
  98. {
  99. #if JUCE_MAC
  100. webView = [[WebView alloc] initWithFrame: NSMakeRect (0, 0, 100.0f, 100.0f)
  101. frameName: nsEmptyString()
  102. groupName: nsEmptyString()];
  103. setView (webView);
  104. static DownloadClickDetectorClass cls;
  105. clickListener = [cls.createInstance() init];
  106. DownloadClickDetectorClass::setOwner (clickListener, owner);
  107. [webView setPolicyDelegate: clickListener];
  108. [webView setFrameLoadDelegate: clickListener];
  109. #else
  110. webView = [[UIWebView alloc] initWithFrame: CGRectMake (0, 0, 1.0f, 1.0f)];
  111. setView (webView);
  112. tapDetector = [[WebViewTapDetector alloc] init];
  113. urlDetector = [[WebViewURLChangeDetector alloc] initWithWebBrowserOwner: owner];
  114. gestureRecogniser = nil;
  115. webView.delegate = urlDetector;
  116. #endif
  117. }
  118. ~Pimpl()
  119. {
  120. #if JUCE_MAC
  121. [webView setPolicyDelegate: nil];
  122. [webView setFrameLoadDelegate: nil];
  123. [clickListener release];
  124. #else
  125. webView.delegate = nil;
  126. [webView removeGestureRecognizer: gestureRecogniser];
  127. [gestureRecogniser release];
  128. [tapDetector release];
  129. [urlDetector release];
  130. #endif
  131. setView (nil);
  132. }
  133. void goToURL (const String& url,
  134. const StringArray* headers,
  135. const MemoryBlock* postData)
  136. {
  137. NSMutableURLRequest* r
  138. = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (url)]
  139. cachePolicy: NSURLRequestUseProtocolCachePolicy
  140. timeoutInterval: 30.0];
  141. if (postData != nullptr && postData->getSize() > 0)
  142. {
  143. [r setHTTPMethod: nsStringLiteral ("POST")];
  144. [r setHTTPBody: [NSData dataWithBytes: postData->getData()
  145. length: postData->getSize()]];
  146. }
  147. if (headers != nullptr)
  148. {
  149. for (int i = 0; i < headers->size(); ++i)
  150. {
  151. const String headerName ((*headers)[i].upToFirstOccurrenceOf (":", false, false).trim());
  152. const String headerValue ((*headers)[i].fromFirstOccurrenceOf (":", false, false).trim());
  153. [r setValue: juceStringToNS (headerValue)
  154. forHTTPHeaderField: juceStringToNS (headerName)];
  155. }
  156. }
  157. stop();
  158. #if JUCE_MAC
  159. [[webView mainFrame] loadRequest: r];
  160. #else
  161. [webView loadRequest: r];
  162. #endif
  163. }
  164. void goBack() { [webView goBack]; }
  165. void goForward() { [webView goForward]; }
  166. #if JUCE_MAC
  167. void stop() { [webView stopLoading: nil]; }
  168. void refresh() { [webView reload: nil]; }
  169. #else
  170. void stop() { [webView stopLoading]; }
  171. void refresh() { [webView reload]; }
  172. #endif
  173. void mouseMove (const MouseEvent&)
  174. {
  175. // WebKit doesn't capture mouse-moves itself, so it seems the only way to make
  176. // them work is to push them via this non-public method..
  177. if ([webView respondsToSelector: @selector (_updateMouseoverWithFakeEvent)])
  178. [webView performSelector: @selector (_updateMouseoverWithFakeEvent)];
  179. }
  180. private:
  181. #if JUCE_MAC
  182. WebView* webView;
  183. NSObject* clickListener;
  184. #else
  185. UIWebView* webView;
  186. WebViewTapDetector* tapDetector;
  187. WebViewURLChangeDetector* urlDetector;
  188. UITapGestureRecognizer* gestureRecogniser;
  189. #endif
  190. };
  191. //==============================================================================
  192. WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
  193. : browser (nullptr),
  194. blankPageShown (false),
  195. unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
  196. {
  197. setOpaque (true);
  198. addAndMakeVisible (browser = new Pimpl (this));
  199. }
  200. WebBrowserComponent::~WebBrowserComponent()
  201. {
  202. deleteAndZero (browser);
  203. }
  204. //==============================================================================
  205. void WebBrowserComponent::goToURL (const String& url,
  206. const StringArray* headers,
  207. const MemoryBlock* postData)
  208. {
  209. lastURL = url;
  210. lastHeaders.clear();
  211. if (headers != nullptr)
  212. lastHeaders = *headers;
  213. lastPostData.setSize (0);
  214. if (postData != nullptr)
  215. lastPostData = *postData;
  216. blankPageShown = false;
  217. browser->goToURL (url, headers, postData);
  218. }
  219. void WebBrowserComponent::stop()
  220. {
  221. browser->stop();
  222. }
  223. void WebBrowserComponent::goBack()
  224. {
  225. lastURL = String::empty;
  226. blankPageShown = false;
  227. browser->goBack();
  228. }
  229. void WebBrowserComponent::goForward()
  230. {
  231. lastURL = String::empty;
  232. browser->goForward();
  233. }
  234. void WebBrowserComponent::refresh()
  235. {
  236. browser->refresh();
  237. }
  238. //==============================================================================
  239. void WebBrowserComponent::paint (Graphics&)
  240. {
  241. }
  242. void WebBrowserComponent::checkWindowAssociation()
  243. {
  244. if (isShowing())
  245. {
  246. if (blankPageShown)
  247. goBack();
  248. }
  249. else
  250. {
  251. if (unloadPageWhenBrowserIsHidden && ! blankPageShown)
  252. {
  253. // when the component becomes invisible, some stuff like flash
  254. // carries on playing audio, so we need to force it onto a blank
  255. // page to avoid this, (and send it back when it's made visible again).
  256. blankPageShown = true;
  257. browser->goToURL ("about:blank", 0, 0);
  258. }
  259. }
  260. }
  261. void WebBrowserComponent::reloadLastURL()
  262. {
  263. if (lastURL.isNotEmpty())
  264. {
  265. goToURL (lastURL, &lastHeaders, &lastPostData);
  266. lastURL = String::empty;
  267. }
  268. }
  269. void WebBrowserComponent::parentHierarchyChanged()
  270. {
  271. checkWindowAssociation();
  272. }
  273. void WebBrowserComponent::resized()
  274. {
  275. browser->setSize (getWidth(), getHeight());
  276. }
  277. void WebBrowserComponent::visibilityChanged()
  278. {
  279. checkWindowAssociation();
  280. }
  281. bool WebBrowserComponent::pageAboutToLoad (const String&) { return true; }
  282. void WebBrowserComponent::pageFinishedLoading (const String&) {}