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.

946 lines
31KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. struct InternalWebViewType
  21. {
  22. InternalWebViewType() {}
  23. virtual ~InternalWebViewType() {}
  24. virtual void createBrowser() = 0;
  25. virtual bool hasBrowserBeenCreated() = 0;
  26. virtual void goToURL (const String&, const StringArray*, const MemoryBlock*) = 0;
  27. virtual void stop() = 0;
  28. virtual void goBack() = 0;
  29. virtual void goForward() = 0;
  30. virtual void refresh() = 0;
  31. virtual void focusGained() {}
  32. virtual void setWebViewSize (int, int) = 0;
  33. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalWebViewType)
  34. };
  35. #if JUCE_MINGW
  36. JUCE_DECLARE_UUID_GETTER (IOleClientSite, "00000118-0000-0000-c000-000000000046")
  37. JUCE_DECLARE_UUID_GETTER (IDispatch, "00020400-0000-0000-c000-000000000046")
  38. #ifndef WebBrowser
  39. class WebBrowser;
  40. #endif
  41. #endif
  42. JUCE_DECLARE_UUID_GETTER (DWebBrowserEvents2, "34A715A0-6587-11D0-924A-0020AFC7AC4D")
  43. JUCE_DECLARE_UUID_GETTER (IConnectionPointContainer, "B196B284-BAB4-101A-B69C-00AA00341D07")
  44. JUCE_DECLARE_UUID_GETTER (IWebBrowser2, "D30C1661-CDAF-11D0-8A3E-00C04FC9E26E")
  45. JUCE_DECLARE_UUID_GETTER (WebBrowser, "8856F961-340A-11D0-A96B-00C04FD705A2")
  46. //==============================================================================
  47. class Win32WebView : public InternalWebViewType,
  48. public ActiveXControlComponent
  49. {
  50. public:
  51. Win32WebView (WebBrowserComponent& owner)
  52. {
  53. owner.addAndMakeVisible (this);
  54. }
  55. ~Win32WebView() override
  56. {
  57. if (connectionPoint != nullptr)
  58. connectionPoint->Unadvise (adviseCookie);
  59. if (browser != nullptr)
  60. browser->Release();
  61. }
  62. void createBrowser() override
  63. {
  64. auto webCLSID = __uuidof (WebBrowser);
  65. createControl (&webCLSID);
  66. auto iidWebBrowser2 = __uuidof (IWebBrowser2);
  67. auto iidConnectionPointContainer = __uuidof (IConnectionPointContainer);
  68. browser = (IWebBrowser2*) queryInterface (&iidWebBrowser2);
  69. if (auto connectionPointContainer = (IConnectionPointContainer*) queryInterface (&iidConnectionPointContainer))
  70. {
  71. connectionPointContainer->FindConnectionPoint (__uuidof (DWebBrowserEvents2), &connectionPoint);
  72. if (connectionPoint != nullptr)
  73. {
  74. auto* owner = dynamic_cast<WebBrowserComponent*> (Component::getParentComponent());
  75. jassert (owner != nullptr);
  76. auto handler = new EventHandler (*owner);
  77. connectionPoint->Advise (handler, &adviseCookie);
  78. handler->Release();
  79. }
  80. }
  81. }
  82. bool hasBrowserBeenCreated() override
  83. {
  84. return browser != nullptr;
  85. }
  86. void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
  87. {
  88. if (browser != nullptr)
  89. {
  90. VARIANT headerFlags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
  91. VariantInit (&headerFlags);
  92. VariantInit (&frame);
  93. VariantInit (&postDataVar);
  94. VariantInit (&headersVar);
  95. if (headers != nullptr)
  96. {
  97. V_VT (&headersVar) = VT_BSTR;
  98. V_BSTR (&headersVar) = SysAllocString ((const OLECHAR*) headers->joinIntoString ("\r\n").toWideCharPointer());
  99. }
  100. if (postData != nullptr && postData->getSize() > 0)
  101. {
  102. auto sa = SafeArrayCreateVector (VT_UI1, 0, (ULONG) postData->getSize());
  103. if (sa != nullptr)
  104. {
  105. void* data = nullptr;
  106. SafeArrayAccessData (sa, &data);
  107. jassert (data != nullptr);
  108. if (data != nullptr)
  109. {
  110. postData->copyTo (data, 0, postData->getSize());
  111. SafeArrayUnaccessData (sa);
  112. VARIANT postDataVar2;
  113. VariantInit (&postDataVar2);
  114. V_VT (&postDataVar2) = VT_ARRAY | VT_UI1;
  115. V_ARRAY (&postDataVar2) = sa;
  116. sa = nullptr;
  117. postDataVar = postDataVar2;
  118. }
  119. else
  120. {
  121. SafeArrayDestroy (sa);
  122. }
  123. }
  124. }
  125. auto urlBSTR = SysAllocString ((const OLECHAR*) url.toWideCharPointer());
  126. browser->Navigate (urlBSTR, &headerFlags, &frame, &postDataVar, &headersVar);
  127. SysFreeString (urlBSTR);
  128. VariantClear (&headerFlags);
  129. VariantClear (&frame);
  130. VariantClear (&postDataVar);
  131. VariantClear (&headersVar);
  132. }
  133. }
  134. void stop() override
  135. {
  136. if (browser != nullptr)
  137. browser->Stop();
  138. }
  139. void goBack() override
  140. {
  141. if (browser != nullptr)
  142. browser->GoBack();
  143. }
  144. void goForward() override
  145. {
  146. if (browser != nullptr)
  147. browser->GoForward();
  148. }
  149. void refresh() override
  150. {
  151. if (browser != nullptr)
  152. browser->Refresh();
  153. }
  154. void focusGained() override
  155. {
  156. auto iidOleObject = __uuidof (IOleObject);
  157. auto iidOleWindow = __uuidof (IOleWindow);
  158. if (auto oleObject = (IOleObject*) queryInterface (&iidOleObject))
  159. {
  160. if (auto oleWindow = (IOleWindow*) queryInterface (&iidOleWindow))
  161. {
  162. IOleClientSite* oleClientSite = nullptr;
  163. if (SUCCEEDED (oleObject->GetClientSite (&oleClientSite)))
  164. {
  165. HWND hwnd;
  166. oleWindow->GetWindow (&hwnd);
  167. oleObject->DoVerb (OLEIVERB_UIACTIVATE, nullptr, oleClientSite, 0, hwnd, nullptr);
  168. oleClientSite->Release();
  169. }
  170. oleWindow->Release();
  171. }
  172. oleObject->Release();
  173. }
  174. }
  175. using ActiveXControlComponent::focusGained;
  176. void setWebViewSize (int width, int height) override
  177. {
  178. setSize (width, height);
  179. }
  180. private:
  181. IWebBrowser2* browser = nullptr;
  182. IConnectionPoint* connectionPoint = nullptr;
  183. DWORD adviseCookie = 0;
  184. //==============================================================================
  185. struct EventHandler : public ComBaseClassHelper<IDispatch>,
  186. public ComponentMovementWatcher
  187. {
  188. EventHandler (WebBrowserComponent& w) : ComponentMovementWatcher (&w), owner (w) {}
  189. JUCE_COMRESULT GetTypeInfoCount (UINT*) override { return E_NOTIMPL; }
  190. JUCE_COMRESULT GetTypeInfo (UINT, LCID, ITypeInfo**) override { return E_NOTIMPL; }
  191. JUCE_COMRESULT GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*) override { return E_NOTIMPL; }
  192. JUCE_COMRESULT Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* pDispParams,
  193. VARIANT* /*pVarResult*/, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/) override
  194. {
  195. if (dispIdMember == DISPID_BEFORENAVIGATE2)
  196. {
  197. *pDispParams->rgvarg->pboolVal
  198. = owner.pageAboutToLoad (getStringFromVariant (pDispParams->rgvarg[5].pvarVal)) ? VARIANT_FALSE
  199. : VARIANT_TRUE;
  200. return S_OK;
  201. }
  202. if (dispIdMember == 273 /*DISPID_NEWWINDOW3*/)
  203. {
  204. owner.newWindowAttemptingToLoad (pDispParams->rgvarg[0].bstrVal);
  205. *pDispParams->rgvarg[3].pboolVal = VARIANT_TRUE;
  206. return S_OK;
  207. }
  208. if (dispIdMember == DISPID_DOCUMENTCOMPLETE)
  209. {
  210. owner.pageFinishedLoading (getStringFromVariant (pDispParams->rgvarg[0].pvarVal));
  211. return S_OK;
  212. }
  213. if (dispIdMember == 271 /*DISPID_NAVIGATEERROR*/)
  214. {
  215. int statusCode = pDispParams->rgvarg[1].pvarVal->intVal;
  216. *pDispParams->rgvarg[0].pboolVal = VARIANT_FALSE;
  217. // IWebBrowser2 also reports http status codes here, we need
  218. // report only network errors
  219. if (statusCode < 0)
  220. {
  221. LPTSTR messageBuffer = nullptr;
  222. auto size = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  223. nullptr, statusCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  224. (LPTSTR) &messageBuffer, 0, nullptr);
  225. String message (messageBuffer, size);
  226. LocalFree (messageBuffer);
  227. if (! owner.pageLoadHadNetworkError (message))
  228. *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
  229. }
  230. return S_OK;
  231. }
  232. if (dispIdMember == 263 /*DISPID_WINDOWCLOSING*/)
  233. {
  234. owner.windowCloseRequest();
  235. // setting this bool tells the browser to ignore the event - we'll handle it.
  236. if (pDispParams->cArgs > 0 && pDispParams->rgvarg[0].vt == (VT_BYREF | VT_BOOL))
  237. *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
  238. return S_OK;
  239. }
  240. return E_NOTIMPL;
  241. }
  242. void componentMovedOrResized (bool, bool) override {}
  243. void componentPeerChanged() override {}
  244. void componentVisibilityChanged() override { owner.visibilityChanged(); }
  245. private:
  246. WebBrowserComponent& owner;
  247. static String getStringFromVariant (VARIANT* v)
  248. {
  249. return (v->vt & VT_BYREF) != 0 ? *v->pbstrVal
  250. : v->bstrVal;
  251. }
  252. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler)
  253. };
  254. //==============================================================================
  255. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32WebView)
  256. };
  257. #if JUCE_USE_WIN_WEBVIEW2
  258. using namespace Microsoft::WRL;
  259. class WebView2 : public InternalWebViewType,
  260. public Component,
  261. public ComponentMovementWatcher
  262. {
  263. public:
  264. WebView2 (WebBrowserComponent& o, const File& dllLocation, const File& userDataFolder)
  265. : ComponentMovementWatcher (&o),
  266. owner (o)
  267. {
  268. if (! createWebViewEnvironment (dllLocation, userDataFolder))
  269. throw std::runtime_error ("Failed to create the CoreWebView2Environemnt");
  270. owner.addAndMakeVisible (this);
  271. }
  272. ~WebView2() override
  273. {
  274. removeEventHandlers();
  275. closeWebView();
  276. if (webView2LoaderHandle != nullptr)
  277. ::FreeLibrary (webView2LoaderHandle);
  278. }
  279. void createBrowser() override
  280. {
  281. if (webView == nullptr)
  282. {
  283. jassert (webViewEnvironment != nullptr);
  284. createWebView();
  285. }
  286. }
  287. bool hasBrowserBeenCreated() override
  288. {
  289. return webView != nullptr || isCreating;
  290. }
  291. void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
  292. {
  293. urlRequest = { url,
  294. headers != nullptr ? *headers : StringArray(),
  295. postData != nullptr && postData->getSize() > 0 ? *postData : MemoryBlock() };
  296. if (webView != nullptr)
  297. webView->Navigate (urlRequest.url.toWideCharPointer());
  298. }
  299. void stop() override
  300. {
  301. if (webView != nullptr)
  302. webView->Stop();
  303. }
  304. void goBack() override
  305. {
  306. if (webView != nullptr)
  307. {
  308. BOOL canGoBack = false;
  309. webView->get_CanGoBack (&canGoBack);
  310. if (canGoBack)
  311. webView->GoBack();
  312. }
  313. }
  314. void goForward() override
  315. {
  316. if (webView != nullptr)
  317. {
  318. BOOL canGoForward = false;
  319. webView->get_CanGoForward (&canGoForward);
  320. if (canGoForward)
  321. webView->GoForward();
  322. }
  323. }
  324. void refresh() override
  325. {
  326. if (webView != nullptr)
  327. webView->Reload();
  328. }
  329. void setWebViewSize (int width, int height) override
  330. {
  331. setSize (width, height);
  332. }
  333. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  334. {
  335. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  336. setControlBounds (peer->getAreaCoveredBy (owner));
  337. }
  338. void componentPeerChanged() override
  339. {
  340. componentMovedOrResized (true, true);
  341. }
  342. void componentVisibilityChanged() override
  343. {
  344. setControlVisible (owner.isShowing());
  345. componentPeerChanged();
  346. owner.visibilityChanged();
  347. }
  348. private:
  349. //==============================================================================
  350. template<class ArgType>
  351. static String getUriStringFromArgs (ArgType* args)
  352. {
  353. if (args != nullptr)
  354. {
  355. LPWSTR uri;
  356. args->get_Uri (&uri);
  357. return uri;
  358. }
  359. return {};
  360. }
  361. //==============================================================================
  362. void addEventHandlers()
  363. {
  364. if (webView != nullptr)
  365. {
  366. webView->add_NavigationStarting (Callback<ICoreWebView2NavigationStartingEventHandler> (
  367. [this] (ICoreWebView2*, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
  368. {
  369. auto uriString = getUriStringFromArgs (args);
  370. if (uriString.isNotEmpty() && ! owner.pageAboutToLoad (uriString))
  371. args->put_Cancel (true);
  372. return S_OK;
  373. }).Get(), &navigationStartingToken);
  374. webView->add_NewWindowRequested (Callback<ICoreWebView2NewWindowRequestedEventHandler> (
  375. [this] (ICoreWebView2*, ICoreWebView2NewWindowRequestedEventArgs* args) -> HRESULT
  376. {
  377. auto uriString = getUriStringFromArgs (args);
  378. if (uriString.isNotEmpty())
  379. {
  380. owner.newWindowAttemptingToLoad (uriString);
  381. args->put_Handled (true);
  382. }
  383. return S_OK;
  384. }).Get(), &newWindowRequestedToken);
  385. webView->add_WindowCloseRequested (Callback<ICoreWebView2WindowCloseRequestedEventHandler> (
  386. [this] (ICoreWebView2*, IUnknown*) -> HRESULT
  387. {
  388. owner.windowCloseRequest();
  389. return S_OK;
  390. }).Get(), &windowCloseRequestedToken);
  391. webView->add_NavigationCompleted (Callback<ICoreWebView2NavigationCompletedEventHandler> (
  392. [this] (ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT
  393. {
  394. LPWSTR uri;
  395. sender->get_Source (&uri);
  396. String uriString (uri);
  397. if (uriString.isNotEmpty())
  398. {
  399. BOOL success = false;
  400. args->get_IsSuccess (&success);
  401. COREWEBVIEW2_WEB_ERROR_STATUS errorStatus;
  402. args->get_WebErrorStatus (&errorStatus);
  403. if (success
  404. || errorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED) // this error seems to happen erroneously so ignore
  405. {
  406. owner.pageFinishedLoading (uriString);
  407. }
  408. else
  409. {
  410. auto errorString = "Error code: " + String (errorStatus);
  411. if (owner.pageLoadHadNetworkError (errorString))
  412. owner.goToURL ("data:text/plain;charset=UTF-8," + errorString);
  413. }
  414. }
  415. return S_OK;
  416. }).Get(), &navigationCompletedToken);
  417. webView->AddWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
  418. webView->add_WebResourceRequested (Callback<ICoreWebView2WebResourceRequestedEventHandler> (
  419. [this] (ICoreWebView2*, ICoreWebView2WebResourceRequestedEventArgs* args) -> HRESULT
  420. {
  421. if (urlRequest.url.isEmpty())
  422. return S_OK;
  423. ComSmartPtr<ICoreWebView2WebResourceRequest> request;
  424. args->get_Request (request.resetAndGetPointerAddress());
  425. auto uriString = getUriStringFromArgs (request.get());
  426. if (uriString == urlRequest.url
  427. || (uriString.endsWith ("/") && uriString.upToLastOccurrenceOf ("/", false, false) == urlRequest.url))
  428. {
  429. String method ("GET");
  430. if (urlRequest.postData.getSize() > 0)
  431. {
  432. method = "POST";
  433. ComSmartPtr<IStream> content (SHCreateMemStream ((BYTE*) urlRequest.postData.getData(),
  434. (UINT) urlRequest.postData.getSize()));
  435. request->put_Content (content.get());
  436. }
  437. if (! urlRequest.headers.isEmpty())
  438. {
  439. ComSmartPtr<ICoreWebView2HttpRequestHeaders> headers;
  440. request->get_Headers (headers.resetAndGetPointerAddress());
  441. for (auto& header : urlRequest.headers)
  442. {
  443. headers->SetHeader (header.upToFirstOccurrenceOf (":", false, false).trim().toWideCharPointer(),
  444. header.fromFirstOccurrenceOf (":", false, false).trim().toWideCharPointer());
  445. }
  446. }
  447. request->put_Method (method.toWideCharPointer());
  448. urlRequest = {};
  449. }
  450. return S_OK;
  451. }).Get(), &webResourceRequestedToken);
  452. }
  453. }
  454. void removeEventHandlers()
  455. {
  456. if (webView != nullptr)
  457. {
  458. if (navigationStartingToken.value != 0)
  459. webView->remove_NavigationStarting (navigationStartingToken);
  460. if (newWindowRequestedToken.value != 0)
  461. webView->remove_NewWindowRequested (newWindowRequestedToken);
  462. if (windowCloseRequestedToken.value != 0)
  463. webView->remove_WindowCloseRequested (windowCloseRequestedToken);
  464. if (navigationCompletedToken.value != 0)
  465. webView->remove_NavigationCompleted (navigationCompletedToken);
  466. if (webResourceRequestedToken.value != 0)
  467. {
  468. webView->RemoveWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
  469. webView->remove_WebResourceRequested (webResourceRequestedToken);
  470. }
  471. }
  472. }
  473. bool createWebViewEnvironment (const File& dllLocation, const File& userDataFolder)
  474. {
  475. using CreateWebViewEnvironmentWithOptionsFunc = HRESULT (*) (PCWSTR, PCWSTR,
  476. ICoreWebView2EnvironmentOptions*,
  477. ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*);
  478. auto dllPath = dllLocation.getFullPathName();
  479. if (dllPath.isEmpty())
  480. dllPath = "WebView2Loader.dll";
  481. webView2LoaderHandle = LoadLibraryA (dllPath.toUTF8());
  482. if (webView2LoaderHandle == nullptr)
  483. return false;
  484. auto* createWebViewEnvironmentWithOptions = (CreateWebViewEnvironmentWithOptionsFunc) GetProcAddress (webView2LoaderHandle,
  485. "CreateCoreWebView2EnvironmentWithOptions");
  486. if (createWebViewEnvironmentWithOptions == nullptr)
  487. {
  488. // failed to load WebView2Loader.dll
  489. jassertfalse;
  490. return false;
  491. }
  492. auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
  493. WeakReference<WebView2> weakThis (this);
  494. auto hr = createWebViewEnvironmentWithOptions (nullptr,
  495. userDataFolder != File() ? userDataFolder.getFullPathName().toWideCharPointer() : nullptr,
  496. options.Get(),
  497. Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
  498. [this, weakThis] (HRESULT, ICoreWebView2Environment* env) -> HRESULT
  499. {
  500. if (weakThis != nullptr)
  501. webViewEnvironment = env;
  502. return S_OK;
  503. }).Get());
  504. return SUCCEEDED (hr);
  505. }
  506. void createWebView()
  507. {
  508. if (auto* peer = getPeer())
  509. {
  510. isCreating = true;
  511. WeakReference<WebView2> weakThis (this);
  512. webViewEnvironment->CreateCoreWebView2Controller ((HWND) peer->getNativeHandle(),
  513. Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler> (
  514. [this, weakThis] (HRESULT, ICoreWebView2Controller* controller) -> HRESULT
  515. {
  516. if (weakThis != nullptr)
  517. {
  518. isCreating = false;
  519. if (controller != nullptr)
  520. {
  521. webViewController = controller;
  522. controller->get_CoreWebView2 (webView.resetAndGetPointerAddress());
  523. addEventHandlers();
  524. componentMovedOrResized (true, true);
  525. if (webView != nullptr && urlRequest.url.isNotEmpty())
  526. webView->Navigate (urlRequest.url.toWideCharPointer());
  527. }
  528. }
  529. return S_OK;
  530. }).Get());
  531. }
  532. }
  533. void closeWebView()
  534. {
  535. if (webViewController.get() != nullptr)
  536. {
  537. webViewController->Close();
  538. webViewController = nullptr;
  539. webView = nullptr;
  540. }
  541. webViewEnvironment = nullptr;
  542. }
  543. //==============================================================================
  544. void setControlBounds (Rectangle<int> newBounds) const
  545. {
  546. if (webViewController != nullptr)
  547. {
  548. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  549. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  550. newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
  551. #endif
  552. webViewController->put_Bounds({ newBounds.getX(), newBounds.getY(),
  553. newBounds.getRight(), newBounds.getBottom() });
  554. }
  555. }
  556. void setControlVisible (bool shouldBeVisible) const
  557. {
  558. if (webViewController != nullptr)
  559. webViewController->put_IsVisible (shouldBeVisible);
  560. }
  561. //==============================================================================
  562. WebBrowserComponent& owner;
  563. HMODULE webView2LoaderHandle = nullptr;
  564. ComSmartPtr<ICoreWebView2Environment> webViewEnvironment;
  565. ComSmartPtr<ICoreWebView2Controller> webViewController;
  566. ComSmartPtr<ICoreWebView2> webView;
  567. EventRegistrationToken navigationStartingToken { 0 },
  568. newWindowRequestedToken { 0 },
  569. windowCloseRequestedToken { 0 },
  570. navigationCompletedToken { 0 },
  571. webResourceRequestedToken { 0 };
  572. struct URLRequest
  573. {
  574. String url;
  575. StringArray headers;
  576. MemoryBlock postData;
  577. };
  578. URLRequest urlRequest;
  579. bool isCreating = false;
  580. //==============================================================================
  581. JUCE_DECLARE_WEAK_REFERENCEABLE (WebView2)
  582. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebView2)
  583. };
  584. #endif
  585. //==============================================================================
  586. class WebBrowserComponent::Pimpl
  587. {
  588. public:
  589. Pimpl (WebBrowserComponent& owner, const File& dllLocation, const File& userDataFolder, bool useWebView2)
  590. {
  591. if (useWebView2)
  592. {
  593. #if JUCE_USE_WIN_WEBVIEW2
  594. try
  595. {
  596. internal.reset (new WebView2 (owner, dllLocation, userDataFolder));
  597. }
  598. catch (std::runtime_error&) {}
  599. #endif
  600. }
  601. ignoreUnused (dllLocation, userDataFolder);
  602. if (internal == nullptr)
  603. internal.reset (new Win32WebView (owner));
  604. }
  605. InternalWebViewType& getInternalWebView()
  606. {
  607. return *internal;
  608. }
  609. private:
  610. std::unique_ptr<InternalWebViewType> internal;
  611. };
  612. //==============================================================================
  613. WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden)
  614. : browser (new Pimpl (*this, {}, {}, false)),
  615. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  616. {
  617. setOpaque (true);
  618. }
  619. WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden,
  620. const File& dllLocation,
  621. const File& userDataFolder)
  622. : browser (new Pimpl (*this, dllLocation, userDataFolder, true)),
  623. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  624. {
  625. setOpaque (true);
  626. }
  627. WebBrowserComponent::~WebBrowserComponent()
  628. {
  629. }
  630. //==============================================================================
  631. void WebBrowserComponent::goToURL (const String& url,
  632. const StringArray* headers,
  633. const MemoryBlock* postData)
  634. {
  635. lastURL = url;
  636. if (headers != nullptr)
  637. lastHeaders = *headers;
  638. else
  639. lastHeaders.clear();
  640. if (postData != nullptr)
  641. lastPostData = *postData;
  642. else
  643. lastPostData.reset();
  644. blankPageShown = false;
  645. if (! browser->getInternalWebView().hasBrowserBeenCreated())
  646. checkWindowAssociation();
  647. browser->getInternalWebView().goToURL (url, headers, postData);
  648. }
  649. void WebBrowserComponent::stop()
  650. {
  651. browser->getInternalWebView().stop();
  652. }
  653. void WebBrowserComponent::goBack()
  654. {
  655. lastURL.clear();
  656. blankPageShown = false;
  657. browser->getInternalWebView().goBack();
  658. }
  659. void WebBrowserComponent::goForward()
  660. {
  661. lastURL.clear();
  662. browser->getInternalWebView().goForward();
  663. }
  664. void WebBrowserComponent::refresh()
  665. {
  666. browser->getInternalWebView().refresh();
  667. }
  668. //==============================================================================
  669. void WebBrowserComponent::paint (Graphics& g)
  670. {
  671. if (! browser->getInternalWebView().hasBrowserBeenCreated())
  672. {
  673. g.fillAll (Colours::white);
  674. checkWindowAssociation();
  675. }
  676. }
  677. void WebBrowserComponent::checkWindowAssociation()
  678. {
  679. if (isShowing())
  680. {
  681. if (! browser->getInternalWebView().hasBrowserBeenCreated() && getPeer() != nullptr)
  682. {
  683. browser->getInternalWebView().createBrowser();
  684. reloadLastURL();
  685. }
  686. else
  687. {
  688. if (blankPageShown)
  689. goBack();
  690. }
  691. }
  692. else
  693. {
  694. if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
  695. {
  696. // when the component becomes invisible, some stuff like flash
  697. // carries on playing audio, so we need to force it onto a blank
  698. // page to avoid this..
  699. blankPageShown = true;
  700. browser->getInternalWebView().goToURL ("about:blank", 0, 0);
  701. }
  702. }
  703. }
  704. void WebBrowserComponent::reloadLastURL()
  705. {
  706. if (lastURL.isNotEmpty())
  707. {
  708. goToURL (lastURL, &lastHeaders, &lastPostData);
  709. lastURL.clear();
  710. }
  711. }
  712. void WebBrowserComponent::parentHierarchyChanged()
  713. {
  714. checkWindowAssociation();
  715. }
  716. void WebBrowserComponent::resized()
  717. {
  718. browser->getInternalWebView().setWebViewSize (getWidth(), getHeight());
  719. }
  720. void WebBrowserComponent::visibilityChanged()
  721. {
  722. checkWindowAssociation();
  723. }
  724. void WebBrowserComponent::focusGained (FocusChangeType)
  725. {
  726. browser->getInternalWebView().focusGained();
  727. }
  728. void WebBrowserComponent::clearCookies()
  729. {
  730. HeapBlock<::INTERNET_CACHE_ENTRY_INFOA> entry;
  731. ::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFOA);
  732. ::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  733. if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  734. {
  735. entry.realloc (1, entrySize);
  736. urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  737. }
  738. if (urlCacheHandle != nullptr)
  739. {
  740. for (;;)
  741. {
  742. ::DeleteUrlCacheEntryA (entry.getData()->lpszSourceUrlName);
  743. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) == 0)
  744. {
  745. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  746. {
  747. entry.realloc (1, entrySize);
  748. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) != 0)
  749. continue;
  750. }
  751. break;
  752. }
  753. }
  754. FindCloseUrlCache (urlCacheHandle);
  755. }
  756. }
  757. } // namespace juce