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.

995 lines
33KB

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