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.

428 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. #if JUCE_MINGW
  16. JUCE_DECLARE_UUID_GETTER (IOleClientSite, "00000118-0000-0000-c000-000000000046")
  17. JUCE_DECLARE_UUID_GETTER (IDispatch, "00020400-0000-0000-c000-000000000046")
  18. #ifndef WebBrowser
  19. class WebBrowser;
  20. #endif
  21. #endif
  22. JUCE_DECLARE_UUID_GETTER (DWebBrowserEvents2, "34A715A0-6587-11D0-924A-0020AFC7AC4D")
  23. JUCE_DECLARE_UUID_GETTER (IConnectionPointContainer, "B196B284-BAB4-101A-B69C-00AA00341D07")
  24. JUCE_DECLARE_UUID_GETTER (IWebBrowser2, "D30C1661-CDAF-11D0-8A3E-00C04FC9E26E")
  25. JUCE_DECLARE_UUID_GETTER (WebBrowser, "8856F961-340A-11D0-A96B-00C04FD705A2")
  26. class WebBrowserComponent::Pimpl : public ActiveXControlComponent
  27. {
  28. public:
  29. Pimpl() {}
  30. ~Pimpl()
  31. {
  32. if (connectionPoint != nullptr)
  33. connectionPoint->Unadvise (adviseCookie);
  34. if (browser != nullptr)
  35. browser->Release();
  36. }
  37. void createBrowser()
  38. {
  39. auto webCLSID = __uuidof (WebBrowser);
  40. createControl (&webCLSID);
  41. auto iidWebBrowser2 = __uuidof (IWebBrowser2);
  42. auto iidConnectionPointContainer = __uuidof (IConnectionPointContainer);
  43. browser = (IWebBrowser2*) queryInterface (&iidWebBrowser2);
  44. if (auto connectionPointContainer = (IConnectionPointContainer*) queryInterface (&iidConnectionPointContainer))
  45. {
  46. connectionPointContainer->FindConnectionPoint (__uuidof (DWebBrowserEvents2), &connectionPoint);
  47. if (connectionPoint != nullptr)
  48. {
  49. auto* owner = dynamic_cast<WebBrowserComponent*> (getParentComponent());
  50. jassert (owner != nullptr);
  51. auto handler = new EventHandler (*owner);
  52. connectionPoint->Advise (handler, &adviseCookie);
  53. handler->Release();
  54. }
  55. }
  56. }
  57. void goToURL (const String& url,
  58. const StringArray* headers,
  59. const MemoryBlock* postData)
  60. {
  61. if (browser != nullptr)
  62. {
  63. VARIANT headerFlags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
  64. VariantInit (&headerFlags);
  65. VariantInit (&frame);
  66. VariantInit (&postDataVar);
  67. VariantInit (&headersVar);
  68. if (headers != nullptr)
  69. {
  70. V_VT (&headersVar) = VT_BSTR;
  71. V_BSTR (&headersVar) = SysAllocString ((const OLECHAR*) headers->joinIntoString ("\r\n").toWideCharPointer());
  72. }
  73. if (postData != nullptr && postData->getSize() > 0)
  74. {
  75. auto sa = SafeArrayCreateVector (VT_UI1, 0, (ULONG) postData->getSize());
  76. if (sa != nullptr)
  77. {
  78. void* data = nullptr;
  79. SafeArrayAccessData (sa, &data);
  80. jassert (data != nullptr);
  81. if (data != nullptr)
  82. {
  83. postData->copyTo (data, 0, postData->getSize());
  84. SafeArrayUnaccessData (sa);
  85. VARIANT postDataVar2;
  86. VariantInit (&postDataVar2);
  87. V_VT (&postDataVar2) = VT_ARRAY | VT_UI1;
  88. V_ARRAY (&postDataVar2) = sa;
  89. sa = nullptr;
  90. postDataVar = postDataVar2;
  91. }
  92. else
  93. {
  94. SafeArrayDestroy (sa);
  95. }
  96. }
  97. }
  98. auto urlBSTR = SysAllocString ((const OLECHAR*) url.toWideCharPointer());
  99. browser->Navigate (urlBSTR, &headerFlags, &frame, &postDataVar, &headersVar);
  100. SysFreeString (urlBSTR);
  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*) override { return E_NOTIMPL; }
  118. JUCE_COMRESULT GetTypeInfo (UINT, LCID, ITypeInfo**) override { return E_NOTIMPL; }
  119. JUCE_COMRESULT GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*) override { return E_NOTIMPL; }
  120. JUCE_COMRESULT Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* pDispParams,
  121. VARIANT* /*pVarResult*/, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/) override
  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 errors
  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 (new Pimpl()),
  187. blankPageShown (false),
  188. unloadPageWhenBrowserIsHidden (unloadPageWhenBrowserIsHidden_)
  189. {
  190. setOpaque (true);
  191. addAndMakeVisible (browser.get());
  192. }
  193. WebBrowserComponent::~WebBrowserComponent()
  194. {
  195. }
  196. //==============================================================================
  197. void WebBrowserComponent::goToURL (const String& url,
  198. const StringArray* headers,
  199. const MemoryBlock* postData)
  200. {
  201. lastURL = url;
  202. if (headers != nullptr)
  203. lastHeaders = *headers;
  204. else
  205. lastHeaders.clear();
  206. if (postData != nullptr)
  207. lastPostData = *postData;
  208. else
  209. lastPostData.reset();
  210. blankPageShown = false;
  211. if (browser->browser == nullptr)
  212. checkWindowAssociation();
  213. browser->goToURL (url, headers, postData);
  214. }
  215. void WebBrowserComponent::stop()
  216. {
  217. if (browser->browser != nullptr)
  218. browser->browser->Stop();
  219. }
  220. void WebBrowserComponent::goBack()
  221. {
  222. lastURL.clear();
  223. blankPageShown = false;
  224. if (browser->browser != nullptr)
  225. browser->browser->GoBack();
  226. }
  227. void WebBrowserComponent::goForward()
  228. {
  229. lastURL.clear();
  230. if (browser->browser != nullptr)
  231. browser->browser->GoForward();
  232. }
  233. void WebBrowserComponent::refresh()
  234. {
  235. if (browser->browser != nullptr)
  236. browser->browser->Refresh();
  237. }
  238. //==============================================================================
  239. void WebBrowserComponent::paint (Graphics& g)
  240. {
  241. if (browser->browser == nullptr)
  242. {
  243. g.fillAll (Colours::white);
  244. checkWindowAssociation();
  245. }
  246. }
  247. void WebBrowserComponent::checkWindowAssociation()
  248. {
  249. if (isShowing())
  250. {
  251. if (browser->browser == nullptr && getPeer() != nullptr)
  252. {
  253. browser->createBrowser();
  254. reloadLastURL();
  255. }
  256. else
  257. {
  258. if (blankPageShown)
  259. goBack();
  260. }
  261. }
  262. else
  263. {
  264. if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
  265. {
  266. // when the component becomes invisible, some stuff like flash
  267. // carries on playing audio, so we need to force it onto a blank
  268. // page to avoid this..
  269. blankPageShown = true;
  270. browser->goToURL ("about:blank", 0, 0);
  271. }
  272. }
  273. }
  274. void WebBrowserComponent::reloadLastURL()
  275. {
  276. if (lastURL.isNotEmpty())
  277. {
  278. goToURL (lastURL, &lastHeaders, &lastPostData);
  279. lastURL.clear();
  280. }
  281. }
  282. void WebBrowserComponent::parentHierarchyChanged()
  283. {
  284. checkWindowAssociation();
  285. }
  286. void WebBrowserComponent::resized()
  287. {
  288. browser->setSize (getWidth(), getHeight());
  289. }
  290. void WebBrowserComponent::visibilityChanged()
  291. {
  292. checkWindowAssociation();
  293. }
  294. void WebBrowserComponent::focusGained (FocusChangeType)
  295. {
  296. auto iidOleObject = __uuidof (IOleObject);
  297. auto iidOleWindow = __uuidof (IOleWindow);
  298. if (auto oleObject = (IOleObject*) browser->queryInterface (&iidOleObject))
  299. {
  300. if (auto oleWindow = (IOleWindow*) browser->queryInterface (&iidOleWindow))
  301. {
  302. IOleClientSite* oleClientSite = nullptr;
  303. if (SUCCEEDED (oleObject->GetClientSite (&oleClientSite)))
  304. {
  305. HWND hwnd;
  306. oleWindow->GetWindow (&hwnd);
  307. oleObject->DoVerb (OLEIVERB_UIACTIVATE, nullptr, oleClientSite, 0, hwnd, nullptr);
  308. oleClientSite->Release();
  309. }
  310. oleWindow->Release();
  311. }
  312. oleObject->Release();
  313. }
  314. }
  315. void WebBrowserComponent::clearCookies()
  316. {
  317. HeapBlock<::INTERNET_CACHE_ENTRY_INFOA> entry;
  318. ::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFOA);
  319. ::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  320. if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  321. {
  322. entry.realloc (1, entrySize);
  323. urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  324. }
  325. if (urlCacheHandle != nullptr)
  326. {
  327. for (;;)
  328. {
  329. ::DeleteUrlCacheEntryA (entry.getData()->lpszSourceUrlName);
  330. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) == 0)
  331. {
  332. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  333. {
  334. entry.realloc (1, entrySize);
  335. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) != 0)
  336. continue;
  337. }
  338. break;
  339. }
  340. }
  341. FindCloseUrlCache (urlCacheHandle);
  342. }
  343. }
  344. } // namespace juce