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.

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