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.

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