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.

403 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI 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. JUCE_COMCLASS (DWebBrowserEvents2, "34A715A0-6587-11D0-924A-0020AFC7AC4D")
  18. JUCE_COMCLASS (IConnectionPointContainer, "B196B284-BAB4-101A-B69C-00AA00341D07")
  19. JUCE_COMCLASS (IWebBrowser2, "D30C1661-CDAF-11D0-8A3E-00C04FC9E26E")
  20. JUCE_COMCLASS (WebBrowser, "8856F961-340A-11D0-A96B-00C04FD705A2")
  21. class WebBrowserComponent::Pimpl : public ActiveXControlComponent
  22. {
  23. public:
  24. Pimpl()
  25. : browser (nullptr),
  26. connectionPoint (nullptr),
  27. adviseCookie (0)
  28. {
  29. }
  30. ~Pimpl()
  31. {
  32. if (connectionPoint != nullptr)
  33. connectionPoint->Unadvise (adviseCookie);
  34. if (browser != nullptr)
  35. browser->Release();
  36. }
  37. void createBrowser()
  38. {
  39. CLSID webCLSID = __uuidof (WebBrowser);
  40. createControl (&webCLSID);
  41. GUID iidWebBrowser2 = __uuidof (IWebBrowser2);
  42. GUID iidConnectionPointContainer = __uuidof (IConnectionPointContainer);
  43. browser = (IWebBrowser2*) queryInterface (&iidWebBrowser2);
  44. if (IConnectionPointContainer* connectionPointContainer
  45. = (IConnectionPointContainer*) queryInterface (&iidConnectionPointContainer))
  46. {
  47. connectionPointContainer->FindConnectionPoint (__uuidof (DWebBrowserEvents2), &connectionPoint);
  48. if (connectionPoint != nullptr)
  49. {
  50. WebBrowserComponent* const owner = dynamic_cast<WebBrowserComponent*> (getParentComponent());
  51. jassert (owner != nullptr);
  52. EventHandler* handler = new EventHandler (*owner);
  53. connectionPoint->Advise (handler, &adviseCookie);
  54. handler->Release();
  55. }
  56. }
  57. }
  58. void goToURL (const String& url,
  59. const StringArray* headers,
  60. const MemoryBlock* postData)
  61. {
  62. if (browser != nullptr)
  63. {
  64. LPSAFEARRAY sa = nullptr;
  65. VARIANT headerFlags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
  66. VariantInit (&headerFlags);
  67. VariantInit (&frame);
  68. VariantInit (&postDataVar);
  69. VariantInit (&headersVar);
  70. if (headers != nullptr)
  71. {
  72. V_VT (&headersVar) = VT_BSTR;
  73. V_BSTR (&headersVar) = SysAllocString ((const OLECHAR*) headers->joinIntoString ("\r\n").toWideCharPointer());
  74. }
  75. if (postData != nullptr && postData->getSize() > 0)
  76. {
  77. sa = SafeArrayCreateVector (VT_UI1, 0, (ULONG) postData->getSize());
  78. if (sa != nullptr)
  79. {
  80. void* data = nullptr;
  81. SafeArrayAccessData (sa, &data);
  82. jassert (data != nullptr);
  83. if (data != nullptr)
  84. {
  85. postData->copyTo (data, 0, postData->getSize());
  86. SafeArrayUnaccessData (sa);
  87. VARIANT postDataVar2;
  88. VariantInit (&postDataVar2);
  89. V_VT (&postDataVar2) = VT_ARRAY | VT_UI1;
  90. V_ARRAY (&postDataVar2) = sa;
  91. postDataVar = postDataVar2;
  92. }
  93. }
  94. }
  95. BSTR urlBSTR = SysAllocString ((const OLECHAR*) url.toWideCharPointer());
  96. browser->Navigate (urlBSTR, &headerFlags, &frame, &postDataVar, &headersVar);
  97. SysFreeString (urlBSTR);
  98. if (sa != nullptr)
  99. SafeArrayDestroy (sa);
  100. VariantClear (&headerFlags);
  101. VariantClear (&frame);
  102. VariantClear (&postDataVar);
  103. VariantClear (&headersVar);
  104. }
  105. }
  106. //==============================================================================
  107. IWebBrowser2* browser;
  108. private:
  109. IConnectionPoint* connectionPoint;
  110. DWORD adviseCookie;
  111. //==============================================================================
  112. struct EventHandler : public ComBaseClassHelper<IDispatch>,
  113. public ComponentMovementWatcher
  114. {
  115. EventHandler (WebBrowserComponent& w) : ComponentMovementWatcher (&w), owner (w) {}
  116. JUCE_COMRESULT GetTypeInfoCount (UINT*) { return E_NOTIMPL; }
  117. JUCE_COMRESULT GetTypeInfo (UINT, LCID, ITypeInfo**) { return E_NOTIMPL; }
  118. JUCE_COMRESULT GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*) { return E_NOTIMPL; }
  119. JUCE_COMRESULT Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* pDispParams,
  120. VARIANT* /*pVarResult*/, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/)
  121. {
  122. if (dispIdMember == DISPID_BEFORENAVIGATE2)
  123. {
  124. *pDispParams->rgvarg->pboolVal
  125. = owner.pageAboutToLoad (getStringFromVariant (pDispParams->rgvarg[5].pvarVal)) ? VARIANT_FALSE
  126. : VARIANT_TRUE;
  127. return S_OK;
  128. }
  129. if (dispIdMember == 273 /*DISPID_NEWWINDOW3*/)
  130. {
  131. owner.newWindowAttemptingToLoad (pDispParams->rgvarg[0].bstrVal);
  132. *pDispParams->rgvarg[3].pboolVal = VARIANT_TRUE;
  133. return S_OK;
  134. }
  135. if (dispIdMember == DISPID_DOCUMENTCOMPLETE)
  136. {
  137. owner.pageFinishedLoading (getStringFromVariant (pDispParams->rgvarg[0].pvarVal));
  138. return S_OK;
  139. }
  140. if (dispIdMember == 263 /*DISPID_WINDOWCLOSING*/)
  141. {
  142. owner.windowCloseRequest();
  143. // setting this bool tells the browser to ignore the event - we'll handle it.
  144. if (pDispParams->cArgs > 0 && pDispParams->rgvarg[0].vt == (VT_BYREF | VT_BOOL))
  145. *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
  146. return S_OK;
  147. }
  148. return E_NOTIMPL;
  149. }
  150. void componentMovedOrResized (bool, bool) override {}
  151. void componentPeerChanged() override {}
  152. void componentVisibilityChanged() override { owner.visibilityChanged(); }
  153. private:
  154. WebBrowserComponent& owner;
  155. static String getStringFromVariant (VARIANT* v)
  156. {
  157. return (v->vt & VT_BYREF) != 0 ? *v->pbstrVal
  158. : v->bstrVal;
  159. }
  160. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler)
  161. };
  162. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  163. };
  164. //==============================================================================
  165. WebBrowserComponent::WebBrowserComponent (const bool unloadPageWhenBrowserIsHidden_)
  166. : browser (nullptr),
  167. blankPageShown (false),
  168. unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
  169. {
  170. setOpaque (true);
  171. addAndMakeVisible (browser = new Pimpl());
  172. }
  173. WebBrowserComponent::~WebBrowserComponent()
  174. {
  175. delete browser;
  176. }
  177. //==============================================================================
  178. void WebBrowserComponent::goToURL (const String& url,
  179. const StringArray* headers,
  180. const MemoryBlock* postData)
  181. {
  182. lastURL = url;
  183. if (headers != nullptr)
  184. lastHeaders = *headers;
  185. else
  186. lastHeaders.clear();
  187. if (postData != nullptr)
  188. lastPostData = *postData;
  189. else
  190. lastPostData.reset();
  191. blankPageShown = false;
  192. if (browser->browser == nullptr)
  193. checkWindowAssociation();
  194. browser->goToURL (url, headers, postData);
  195. }
  196. void WebBrowserComponent::stop()
  197. {
  198. if (browser->browser != nullptr)
  199. browser->browser->Stop();
  200. }
  201. void WebBrowserComponent::goBack()
  202. {
  203. lastURL.clear();
  204. blankPageShown = false;
  205. if (browser->browser != nullptr)
  206. browser->browser->GoBack();
  207. }
  208. void WebBrowserComponent::goForward()
  209. {
  210. lastURL.clear();
  211. if (browser->browser != nullptr)
  212. browser->browser->GoForward();
  213. }
  214. void WebBrowserComponent::refresh()
  215. {
  216. if (browser->browser != nullptr)
  217. browser->browser->Refresh();
  218. }
  219. //==============================================================================
  220. void WebBrowserComponent::paint (Graphics& g)
  221. {
  222. if (browser->browser == nullptr)
  223. {
  224. g.fillAll (Colours::white);
  225. checkWindowAssociation();
  226. }
  227. }
  228. void WebBrowserComponent::checkWindowAssociation()
  229. {
  230. if (isShowing())
  231. {
  232. if (browser->browser == nullptr && getPeer() != nullptr)
  233. {
  234. browser->createBrowser();
  235. reloadLastURL();
  236. }
  237. else
  238. {
  239. if (blankPageShown)
  240. goBack();
  241. }
  242. }
  243. else
  244. {
  245. if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
  246. {
  247. // when the component becomes invisible, some stuff like flash
  248. // carries on playing audio, so we need to force it onto a blank
  249. // page to avoid this..
  250. blankPageShown = true;
  251. browser->goToURL ("about:blank", 0, 0);
  252. }
  253. }
  254. }
  255. void WebBrowserComponent::reloadLastURL()
  256. {
  257. if (lastURL.isNotEmpty())
  258. {
  259. goToURL (lastURL, &lastHeaders, &lastPostData);
  260. lastURL.clear();
  261. }
  262. }
  263. void WebBrowserComponent::parentHierarchyChanged()
  264. {
  265. checkWindowAssociation();
  266. }
  267. void WebBrowserComponent::resized()
  268. {
  269. browser->setSize (getWidth(), getHeight());
  270. }
  271. void WebBrowserComponent::visibilityChanged()
  272. {
  273. checkWindowAssociation();
  274. }
  275. void WebBrowserComponent::focusGained (FocusChangeType)
  276. {
  277. GUID iidOleObject = __uuidof (IOleObject);
  278. GUID iidOleWindow = __uuidof (IOleWindow);
  279. if (IOleObject* oleObject = (IOleObject*) browser->queryInterface (&iidOleObject))
  280. {
  281. if (IOleWindow* oleWindow = (IOleWindow*) browser->queryInterface (&iidOleWindow))
  282. {
  283. IOleClientSite* oleClientSite = nullptr;
  284. if (SUCCEEDED (oleObject->GetClientSite (&oleClientSite)))
  285. {
  286. HWND hwnd;
  287. oleWindow->GetWindow (&hwnd);
  288. oleObject->DoVerb (OLEIVERB_UIACTIVATE, nullptr, oleClientSite, 0, hwnd, nullptr);
  289. oleClientSite->Release();
  290. }
  291. oleWindow->Release();
  292. }
  293. oleObject->Release();
  294. }
  295. }
  296. void WebBrowserComponent::clearCookies()
  297. {
  298. HeapBlock<::INTERNET_CACHE_ENTRY_INFO> entry;
  299. ::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFO);
  300. ::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntry (TEXT ("cookie:"), entry.getData(), &entrySize);
  301. if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  302. {
  303. entry.realloc (1, entrySize);
  304. urlCacheHandle = ::FindFirstUrlCacheEntry (TEXT ("cookie:"), entry.getData(), &entrySize);
  305. }
  306. if (urlCacheHandle != nullptr)
  307. {
  308. for (;;)
  309. {
  310. ::DeleteUrlCacheEntry (entry.getData()->lpszSourceUrlName);
  311. if (::FindNextUrlCacheEntry (urlCacheHandle, entry.getData(), &entrySize) == 0)
  312. {
  313. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  314. {
  315. entry.realloc (1, entrySize);
  316. if (::FindNextUrlCacheEntry (urlCacheHandle, entry.getData(), &entrySize) != 0)
  317. continue;
  318. }
  319. break;
  320. }
  321. }
  322. FindCloseUrlCache (urlCacheHandle);
  323. }
  324. }