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.

443 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  24. #import <Cocoa/Cocoa.h>
  25. #import <WebKit/HIWebView.h>
  26. #import <WebKit/WebPolicyDelegate.h>
  27. #import <WebKit/CarbonUtils.h>
  28. BEGIN_JUCE_NAMESPACE
  29. #include "../../../src/juce_appframework/events/juce_Timer.h"
  30. #include "../../../src/juce_appframework/gui/components/special/juce_WebBrowserComponent.h"
  31. END_JUCE_NAMESPACE
  32. //==============================================================================
  33. @interface DownloadClickDetector : NSObject
  34. {
  35. juce::WebBrowserComponent* ownerComponent;
  36. }
  37. - (DownloadClickDetector*) init: (juce::WebBrowserComponent*) ownerComponent;
  38. - (void) webView: (WebView*) webView decidePolicyForNavigationAction: (NSDictionary*) actionInformation
  39. request: (NSURLRequest*) request
  40. frame: (WebFrame*) frame
  41. decisionListener: (id<WebPolicyDecisionListener>) listener;
  42. @end
  43. //==============================================================================
  44. @implementation DownloadClickDetector
  45. - (DownloadClickDetector*) init: (juce::WebBrowserComponent*) ownerComponent_
  46. {
  47. [super init];
  48. ownerComponent = ownerComponent_;
  49. return self;
  50. }
  51. - (void) webView: (WebView*) sender decidePolicyForNavigationAction: (NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id < WebPolicyDecisionListener >)listener
  52. {
  53. NSURL* url = [actionInformation valueForKey: @"WebActionOriginalURLKey"];
  54. if (ownerComponent->pageAboutToLoad (juce::String::fromUTF8 ((const juce::uint8*) [[url absoluteString] UTF8String])))
  55. [listener use];
  56. else
  57. [listener ignore];
  58. }
  59. @end
  60. BEGIN_JUCE_NAMESPACE
  61. //==============================================================================
  62. class WebBrowserComponentInternal : public Timer
  63. {
  64. public:
  65. WebBrowserComponentInternal (WebBrowserComponent* const owner_)
  66. : owner (owner_),
  67. view (0),
  68. webView (0)
  69. {
  70. HIWebViewCreate (&view);
  71. ComponentPeer* const peer = owner_->getPeer();
  72. jassert (peer != 0);
  73. if (view != 0 && peer != 0)
  74. {
  75. WindowRef parentWindow = (WindowRef) peer->getNativeHandle();
  76. WindowAttributes attributes;
  77. GetWindowAttributes (parentWindow, &attributes);
  78. HIViewRef parentView = 0;
  79. if ((attributes & kWindowCompositingAttribute) != 0)
  80. {
  81. HIViewRef root = HIViewGetRoot (parentWindow);
  82. HIViewFindByID (root, kHIViewWindowContentID, &parentView);
  83. if (parentView == 0)
  84. parentView = root;
  85. }
  86. else
  87. {
  88. GetRootControl (parentWindow, (ControlRef*) &parentView);
  89. if (parentView == 0)
  90. CreateRootControl (parentWindow, (ControlRef*) &parentView);
  91. }
  92. HIViewAddSubview (parentView, view);
  93. updateBounds();
  94. show();
  95. webView = HIWebViewGetWebView (view);
  96. clickListener = [[DownloadClickDetector alloc] init: owner_];
  97. [webView setPolicyDelegate: clickListener];
  98. }
  99. startTimer (500);
  100. }
  101. ~WebBrowserComponentInternal()
  102. {
  103. [webView setPolicyDelegate: nil];
  104. [clickListener release];
  105. if (view != 0)
  106. CFRelease (view);
  107. }
  108. // Horrific bodge-workaround for the fact that the webview somehow hangs onto key
  109. // focus when you pop up a new window, no matter what that window does to
  110. // try to grab focus for itself. This catches such a situation and forces
  111. // focus away from the webview, then back to the place it should be..
  112. void timerCallback()
  113. {
  114. WindowRef viewWindow = HIViewGetWindow (view);
  115. WindowRef focusedWindow = GetUserFocusWindow();
  116. if (focusedWindow != viewWindow)
  117. {
  118. if (HIViewSubtreeContainsFocus (view))
  119. {
  120. HIViewAdvanceFocus (HIViewGetRoot (viewWindow), 0);
  121. HIViewAdvanceFocus (HIViewGetRoot (focusedWindow), 0);
  122. }
  123. }
  124. }
  125. void show()
  126. {
  127. HIViewSetVisible (view, true);
  128. }
  129. void hide()
  130. {
  131. HIViewSetVisible (view, false);
  132. }
  133. void goToURL (const String& url,
  134. const StringArray* headers,
  135. const MemoryBlock* postData)
  136. {
  137. char** headerNamesAsChars = 0;
  138. char** headerValuesAsChars = 0;
  139. int numHeaders = 0;
  140. if (headers != 0)
  141. {
  142. numHeaders = headers->size();
  143. headerNamesAsChars = (char**) juce_malloc (sizeof (char*) * numHeaders);
  144. headerValuesAsChars = (char**) juce_malloc (sizeof (char*) * numHeaders);
  145. int i;
  146. for (i = 0; i < numHeaders; ++i)
  147. {
  148. const String headerName ((*headers)[i].upToFirstOccurrenceOf (T(":"), false, false).trim());
  149. headerNamesAsChars[i] = (char*) juce_calloc (headerName.copyToUTF8 (0));
  150. headerName.copyToUTF8 ((juce::uint8*) headerNamesAsChars[i]);
  151. const String headerValue ((*headers)[i].fromFirstOccurrenceOf (T(":"), false, false).trim());
  152. headerValuesAsChars[i] = (char*) juce_calloc (headerValue.copyToUTF8 (0));
  153. headerValue.copyToUTF8 ((juce::uint8*) headerValuesAsChars[i]);
  154. }
  155. }
  156. sendWebViewToURL ((const char*) url.toUTF8(),
  157. (const char**) headerNamesAsChars,
  158. (const char**) headerValuesAsChars,
  159. numHeaders,
  160. postData != 0 ? (const char*) postData->getData() : 0,
  161. postData != 0 ? postData->getSize() : 0);
  162. for (int i = 0; i < numHeaders; ++i)
  163. {
  164. juce_free (headerNamesAsChars[i]);
  165. juce_free (headerValuesAsChars[i]);
  166. }
  167. juce_free (headerNamesAsChars);
  168. juce_free (headerValuesAsChars);
  169. }
  170. void goBack()
  171. {
  172. [webView goBack];
  173. }
  174. void goForward()
  175. {
  176. [webView goForward];
  177. }
  178. void stop()
  179. {
  180. [webView stopLoading];
  181. }
  182. void updateBounds()
  183. {
  184. HIRect r;
  185. r.origin.x = (float) owner->getScreenX() - owner->getTopLevelComponent()->getScreenX();
  186. r.origin.y = (float) owner->getScreenY() - owner->getTopLevelComponent()->getScreenY();
  187. r.size.width = (float) owner->getWidth();
  188. r.size.height = (float) owner->getHeight();
  189. HIViewSetFrame (view, &r);
  190. }
  191. private:
  192. WebBrowserComponent* const owner;
  193. HIViewRef view;
  194. WebView* webView;
  195. DownloadClickDetector* clickListener;
  196. void sendWebViewToURL (const char* utf8URL,
  197. const char** headerNames,
  198. const char** headerValues,
  199. int numHeaders,
  200. const char* postData,
  201. int postDataSize)
  202. {
  203. NSMutableURLRequest* r = [NSMutableURLRequest
  204. requestWithURL: [NSURL URLWithString: [NSString stringWithUTF8String: utf8URL]]
  205. cachePolicy: NSURLRequestUseProtocolCachePolicy
  206. timeoutInterval: 30.0];
  207. if (postDataSize > 0)
  208. {
  209. [ r setHTTPMethod: @"POST"];
  210. [ r setHTTPBody: [NSData dataWithBytes: postData length: postDataSize]];
  211. }
  212. int i;
  213. for (i = 0; i < numHeaders; ++i)
  214. {
  215. [ r setValue: [NSString stringWithUTF8String: headerValues[i]]
  216. forHTTPHeaderField: [NSString stringWithUTF8String: headerNames[i]]];
  217. }
  218. [[webView mainFrame] stopLoading ];
  219. [[webView mainFrame] loadRequest: r];
  220. }
  221. WebBrowserComponentInternal (const WebBrowserComponentInternal&);
  222. const WebBrowserComponentInternal& operator= (const WebBrowserComponentInternal&);
  223. };
  224. //==============================================================================
  225. WebBrowserComponent::WebBrowserComponent()
  226. : browser (0),
  227. associatedWindow (0),
  228. blankPageShown (false)
  229. {
  230. setOpaque (true);
  231. }
  232. WebBrowserComponent::~WebBrowserComponent()
  233. {
  234. deleteBrowser();
  235. }
  236. //==============================================================================
  237. void WebBrowserComponent::goToURL (const String& url,
  238. const StringArray* headers,
  239. const MemoryBlock* postData)
  240. {
  241. lastURL = url;
  242. lastHeaders.clear();
  243. if (headers != 0)
  244. lastHeaders = *headers;
  245. lastPostData.setSize (0);
  246. if (postData != 0)
  247. lastPostData = *postData;
  248. blankPageShown = false;
  249. if (browser != 0)
  250. browser->goToURL (url, headers, postData);
  251. }
  252. void WebBrowserComponent::stop()
  253. {
  254. if (browser != 0)
  255. browser->stop();
  256. }
  257. void WebBrowserComponent::goBack()
  258. {
  259. lastURL = String::empty;
  260. blankPageShown = false;
  261. if (browser != 0)
  262. browser->goBack();
  263. }
  264. void WebBrowserComponent::goForward()
  265. {
  266. lastURL = String::empty;
  267. if (browser != 0)
  268. browser->goForward();
  269. }
  270. //==============================================================================
  271. void WebBrowserComponent::paint (Graphics& g)
  272. {
  273. if (browser == 0)
  274. g.fillAll (Colours::white);
  275. }
  276. void WebBrowserComponent::checkWindowAssociation()
  277. {
  278. void* const window = getWindowHandle();
  279. if (window != associatedWindow
  280. || (browser == 0 && window != 0))
  281. {
  282. associatedWindow = window;
  283. deleteBrowser();
  284. createBrowser();
  285. }
  286. if (browser != 0)
  287. {
  288. if (associatedWindow != 0 && isShowing())
  289. {
  290. browser->show();
  291. if (blankPageShown)
  292. goBack();
  293. }
  294. else
  295. {
  296. if (! blankPageShown)
  297. {
  298. // when the component becomes invisible, some stuff like flash
  299. // carries on playing audio, so we need to force it onto a blank
  300. // page to avoid this..
  301. blankPageShown = true;
  302. browser->goToURL ("about:blank", 0, 0);
  303. }
  304. browser->hide();
  305. }
  306. }
  307. }
  308. void WebBrowserComponent::createBrowser()
  309. {
  310. deleteBrowser();
  311. if (isShowing())
  312. {
  313. WebInitForCarbon();
  314. browser = new WebBrowserComponentInternal (this);
  315. reloadLastURL();
  316. }
  317. }
  318. void WebBrowserComponent::deleteBrowser()
  319. {
  320. deleteAndZero (browser);
  321. }
  322. void WebBrowserComponent::reloadLastURL()
  323. {
  324. if (lastURL.isNotEmpty())
  325. {
  326. goToURL (lastURL, &lastHeaders, &lastPostData);
  327. lastURL = String::empty;
  328. }
  329. }
  330. void WebBrowserComponent::updateBrowserPosition()
  331. {
  332. if (getPeer() != 0 && browser != 0)
  333. browser->updateBounds();
  334. }
  335. void WebBrowserComponent::parentHierarchyChanged()
  336. {
  337. checkWindowAssociation();
  338. }
  339. void WebBrowserComponent::moved()
  340. {
  341. updateBrowserPosition();
  342. }
  343. void WebBrowserComponent::resized()
  344. {
  345. updateBrowserPosition();
  346. }
  347. void WebBrowserComponent::visibilityChanged()
  348. {
  349. checkWindowAssociation();
  350. }
  351. bool WebBrowserComponent::pageAboutToLoad (const String& url)
  352. {
  353. return true;
  354. }
  355. END_JUCE_NAMESPACE