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.

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