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.

437 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 (nullptr),
  191. blankPageShown (false),
  192. unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
  193. {
  194. setOpaque (true);
  195. addAndMakeVisible (browser = new Pimpl());
  196. }
  197. WebBrowserComponent::~WebBrowserComponent()
  198. {
  199. delete browser;
  200. }
  201. //==============================================================================
  202. void WebBrowserComponent::goToURL (const String& url,
  203. const StringArray* headers,
  204. const MemoryBlock* postData)
  205. {
  206. lastURL = url;
  207. if (headers != nullptr)
  208. lastHeaders = *headers;
  209. else
  210. lastHeaders.clear();
  211. if (postData != nullptr)
  212. lastPostData = *postData;
  213. else
  214. lastPostData.reset();
  215. blankPageShown = false;
  216. if (browser->browser == nullptr)
  217. checkWindowAssociation();
  218. browser->goToURL (url, headers, postData);
  219. }
  220. void WebBrowserComponent::stop()
  221. {
  222. if (browser->browser != nullptr)
  223. browser->browser->Stop();
  224. }
  225. void WebBrowserComponent::goBack()
  226. {
  227. lastURL.clear();
  228. blankPageShown = false;
  229. if (browser->browser != nullptr)
  230. browser->browser->GoBack();
  231. }
  232. void WebBrowserComponent::goForward()
  233. {
  234. lastURL.clear();
  235. if (browser->browser != nullptr)
  236. browser->browser->GoForward();
  237. }
  238. void WebBrowserComponent::refresh()
  239. {
  240. if (browser->browser != nullptr)
  241. browser->browser->Refresh();
  242. }
  243. //==============================================================================
  244. void WebBrowserComponent::paint (Graphics& g)
  245. {
  246. if (browser->browser == nullptr)
  247. {
  248. g.fillAll (Colours::white);
  249. checkWindowAssociation();
  250. }
  251. }
  252. void WebBrowserComponent::checkWindowAssociation()
  253. {
  254. if (isShowing())
  255. {
  256. if (browser->browser == nullptr && getPeer() != nullptr)
  257. {
  258. browser->createBrowser();
  259. reloadLastURL();
  260. }
  261. else
  262. {
  263. if (blankPageShown)
  264. goBack();
  265. }
  266. }
  267. else
  268. {
  269. if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
  270. {
  271. // when the component becomes invisible, some stuff like flash
  272. // carries on playing audio, so we need to force it onto a blank
  273. // page to avoid this..
  274. blankPageShown = true;
  275. browser->goToURL ("about:blank", 0, 0);
  276. }
  277. }
  278. }
  279. void WebBrowserComponent::reloadLastURL()
  280. {
  281. if (lastURL.isNotEmpty())
  282. {
  283. goToURL (lastURL, &lastHeaders, &lastPostData);
  284. lastURL.clear();
  285. }
  286. }
  287. void WebBrowserComponent::parentHierarchyChanged()
  288. {
  289. checkWindowAssociation();
  290. }
  291. void WebBrowserComponent::resized()
  292. {
  293. browser->setSize (getWidth(), getHeight());
  294. }
  295. void WebBrowserComponent::visibilityChanged()
  296. {
  297. checkWindowAssociation();
  298. }
  299. void WebBrowserComponent::focusGained (FocusChangeType)
  300. {
  301. auto iidOleObject = __uuidof (IOleObject);
  302. auto iidOleWindow = __uuidof (IOleWindow);
  303. if (auto oleObject = (IOleObject*) browser->queryInterface (&iidOleObject))
  304. {
  305. if (auto oleWindow = (IOleWindow*) browser->queryInterface (&iidOleWindow))
  306. {
  307. IOleClientSite* oleClientSite = nullptr;
  308. if (SUCCEEDED (oleObject->GetClientSite (&oleClientSite)))
  309. {
  310. HWND hwnd;
  311. oleWindow->GetWindow (&hwnd);
  312. oleObject->DoVerb (OLEIVERB_UIACTIVATE, nullptr, oleClientSite, 0, hwnd, nullptr);
  313. oleClientSite->Release();
  314. }
  315. oleWindow->Release();
  316. }
  317. oleObject->Release();
  318. }
  319. }
  320. void WebBrowserComponent::clearCookies()
  321. {
  322. HeapBlock<::INTERNET_CACHE_ENTRY_INFOA> entry;
  323. ::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFOA);
  324. ::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  325. if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  326. {
  327. entry.realloc (1, entrySize);
  328. urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  329. }
  330. if (urlCacheHandle != nullptr)
  331. {
  332. for (;;)
  333. {
  334. ::DeleteUrlCacheEntryA (entry.getData()->lpszSourceUrlName);
  335. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) == 0)
  336. {
  337. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  338. {
  339. entry.realloc (1, entrySize);
  340. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) != 0)
  341. continue;
  342. }
  343. break;
  344. }
  345. }
  346. FindCloseUrlCache (urlCacheHandle);
  347. }
  348. }
  349. } // namespace juce