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.

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