Audio plugin host https://kx.studio/carla
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.

juce_win32_WebBrowserComponent.cpp 14KB

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