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.

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