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.

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