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.

435 lines
14KB

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