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.

949 lines
32KB

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