Audio plugin host https://kx.studio/carla
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.

juce_win32_WebBrowserComponent.cpp 33KB

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