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 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  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 (! WinRTWrapper::getInstance()->isInitialised())
  269. throw std::runtime_error ("Failed to initialise the WinRT wrapper");
  270. if (! createWebViewEnvironment (dllLocation, userDataFolder))
  271. throw std::runtime_error ("Failed to create the CoreWebView2Environemnt");
  272. owner.addAndMakeVisible (this);
  273. }
  274. ~WebView2() override
  275. {
  276. removeEventHandlers();
  277. closeWebView();
  278. if (webView2LoaderHandle != nullptr)
  279. ::FreeLibrary (webView2LoaderHandle);
  280. }
  281. void createBrowser() override
  282. {
  283. if (webView == nullptr)
  284. {
  285. jassert (webViewEnvironment != nullptr);
  286. createWebView();
  287. }
  288. }
  289. bool hasBrowserBeenCreated() override
  290. {
  291. return webView != nullptr || isCreating;
  292. }
  293. void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
  294. {
  295. urlRequest = { url,
  296. headers != nullptr ? *headers : StringArray(),
  297. postData != nullptr && postData->getSize() > 0 ? *postData : MemoryBlock() };
  298. if (webView != nullptr)
  299. webView->Navigate (urlRequest.url.toWideCharPointer());
  300. }
  301. void stop() override
  302. {
  303. if (webView != nullptr)
  304. webView->Stop();
  305. }
  306. void goBack() override
  307. {
  308. if (webView != nullptr)
  309. {
  310. BOOL canGoBack = false;
  311. webView->get_CanGoBack (&canGoBack);
  312. if (canGoBack)
  313. webView->GoBack();
  314. }
  315. }
  316. void goForward() override
  317. {
  318. if (webView != nullptr)
  319. {
  320. BOOL canGoForward = false;
  321. webView->get_CanGoForward (&canGoForward);
  322. if (canGoForward)
  323. webView->GoForward();
  324. }
  325. }
  326. void refresh() override
  327. {
  328. if (webView != nullptr)
  329. webView->Reload();
  330. }
  331. void setWebViewSize (int width, int height) override
  332. {
  333. setSize (width, height);
  334. }
  335. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  336. {
  337. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  338. setControlBounds (peer->getAreaCoveredBy (owner));
  339. }
  340. void componentPeerChanged() override
  341. {
  342. componentMovedOrResized (true, true);
  343. }
  344. void componentVisibilityChanged() override
  345. {
  346. setControlVisible (owner.isShowing());
  347. componentPeerChanged();
  348. owner.visibilityChanged();
  349. }
  350. private:
  351. //==============================================================================
  352. template<class ArgType>
  353. static String getUriStringFromArgs (ArgType* args)
  354. {
  355. if (args != nullptr)
  356. {
  357. LPWSTR uri;
  358. args->get_Uri (&uri);
  359. return uri;
  360. }
  361. return {};
  362. }
  363. //==============================================================================
  364. void addEventHandlers()
  365. {
  366. if (webView != nullptr)
  367. {
  368. webView->add_NavigationStarting (Callback<ICoreWebView2NavigationStartingEventHandler> (
  369. [this] (ICoreWebView2*, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
  370. {
  371. auto uriString = getUriStringFromArgs (args);
  372. if (uriString.isNotEmpty() && ! owner.pageAboutToLoad (uriString))
  373. args->put_Cancel (true);
  374. return S_OK;
  375. }).Get(), &navigationStartingToken);
  376. webView->add_NewWindowRequested (Callback<ICoreWebView2NewWindowRequestedEventHandler> (
  377. [this] (ICoreWebView2*, ICoreWebView2NewWindowRequestedEventArgs* args) -> HRESULT
  378. {
  379. auto uriString = getUriStringFromArgs (args);
  380. if (uriString.isNotEmpty())
  381. {
  382. owner.newWindowAttemptingToLoad (uriString);
  383. args->put_Handled (true);
  384. }
  385. return S_OK;
  386. }).Get(), &newWindowRequestedToken);
  387. webView->add_WindowCloseRequested (Callback<ICoreWebView2WindowCloseRequestedEventHandler> (
  388. [this] (ICoreWebView2*, IUnknown*) -> HRESULT
  389. {
  390. owner.windowCloseRequest();
  391. return S_OK;
  392. }).Get(), &windowCloseRequestedToken);
  393. webView->add_NavigationCompleted (Callback<ICoreWebView2NavigationCompletedEventHandler> (
  394. [this] (ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT
  395. {
  396. LPWSTR uri;
  397. sender->get_Source (&uri);
  398. String uriString (uri);
  399. if (uriString.isNotEmpty())
  400. {
  401. BOOL success = false;
  402. args->get_IsSuccess (&success);
  403. COREWEBVIEW2_WEB_ERROR_STATUS errorStatus;
  404. args->get_WebErrorStatus (&errorStatus);
  405. if (success
  406. || errorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED) // this error seems to happen erroneously so ignore
  407. {
  408. owner.pageFinishedLoading (uriString);
  409. }
  410. else
  411. {
  412. auto errorString = "Error code: " + String (errorStatus);
  413. if (owner.pageLoadHadNetworkError (errorString))
  414. owner.goToURL ("data:text/plain;charset=UTF-8," + errorString);
  415. }
  416. }
  417. return S_OK;
  418. }).Get(), &navigationCompletedToken);
  419. webView->AddWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
  420. webView->add_WebResourceRequested (Callback<ICoreWebView2WebResourceRequestedEventHandler> (
  421. [this] (ICoreWebView2*, ICoreWebView2WebResourceRequestedEventArgs* args) -> HRESULT
  422. {
  423. if (urlRequest.url.isEmpty())
  424. return S_OK;
  425. WinRTWrapper::ComPtr<ICoreWebView2WebResourceRequest> request;
  426. args->get_Request (request.resetAndGetPointerAddress());
  427. auto uriString = getUriStringFromArgs (request.get());
  428. if (uriString == urlRequest.url
  429. || (uriString.endsWith ("/") && uriString.upToLastOccurrenceOf ("/", false, false) == urlRequest.url))
  430. {
  431. String method ("GET");
  432. if (urlRequest.postData.getSize() > 0)
  433. {
  434. method = "POST";
  435. WinRTWrapper::ComPtr<IStream> content (SHCreateMemStream ((BYTE*) urlRequest.postData.getData(),
  436. (UINT) urlRequest.postData.getSize()));
  437. request->put_Content (content.get());
  438. }
  439. if (! urlRequest.headers.isEmpty())
  440. {
  441. WinRTWrapper::ComPtr<ICoreWebView2HttpRequestHeaders> headers;
  442. request->get_Headers (headers.resetAndGetPointerAddress());
  443. for (auto& header : urlRequest.headers)
  444. {
  445. headers->SetHeader (header.upToFirstOccurrenceOf (":", false, false).trim().toWideCharPointer(),
  446. header.fromFirstOccurrenceOf (":", false, false).trim().toWideCharPointer());
  447. }
  448. }
  449. request->put_Method (method.toWideCharPointer());
  450. urlRequest = {};
  451. }
  452. return S_OK;
  453. }).Get(), &webResourceRequestedToken);
  454. }
  455. }
  456. void removeEventHandlers()
  457. {
  458. if (webView != nullptr)
  459. {
  460. if (navigationStartingToken.value != 0)
  461. webView->remove_NavigationStarting (navigationStartingToken);
  462. if (newWindowRequestedToken.value != 0)
  463. webView->remove_NewWindowRequested (newWindowRequestedToken);
  464. if (windowCloseRequestedToken.value != 0)
  465. webView->remove_WindowCloseRequested (windowCloseRequestedToken);
  466. if (navigationCompletedToken.value != 0)
  467. webView->remove_NavigationCompleted (navigationCompletedToken);
  468. if (webResourceRequestedToken.value != 0)
  469. {
  470. webView->RemoveWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
  471. webView->remove_WebResourceRequested (webResourceRequestedToken);
  472. }
  473. }
  474. }
  475. bool createWebViewEnvironment (const File& dllLocation, const File& userDataFolder)
  476. {
  477. using CreateWebViewEnvironmentWithOptionsFunc = HRESULT (*) (PCWSTR, PCWSTR,
  478. ICoreWebView2EnvironmentOptions*,
  479. ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*);
  480. auto dllPath = dllLocation.getFullPathName();
  481. if (dllPath.isEmpty())
  482. dllPath = "WebView2Loader.dll";
  483. webView2LoaderHandle = LoadLibraryA (dllPath.toUTF8());
  484. if (webView2LoaderHandle == nullptr)
  485. return false;
  486. auto* createWebViewEnvironmentWithOptions = (CreateWebViewEnvironmentWithOptionsFunc) GetProcAddress (webView2LoaderHandle,
  487. "CreateCoreWebView2EnvironmentWithOptions");
  488. if (createWebViewEnvironmentWithOptions == nullptr)
  489. {
  490. // failed to load WebView2Loader.dll
  491. jassertfalse;
  492. return false;
  493. }
  494. auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
  495. WeakReference<WebView2> weakThis (this);
  496. auto hr = createWebViewEnvironmentWithOptions (nullptr,
  497. userDataFolder != File() ? userDataFolder.getFullPathName().toWideCharPointer() : nullptr,
  498. options.Get(),
  499. Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
  500. [this, weakThis] (HRESULT, ICoreWebView2Environment* env) -> HRESULT
  501. {
  502. if (weakThis != nullptr)
  503. webViewEnvironment = env;
  504. return S_OK;
  505. }).Get());
  506. return SUCCEEDED (hr);
  507. }
  508. void createWebView()
  509. {
  510. if (auto* peer = getPeer())
  511. {
  512. isCreating = true;
  513. WeakReference<WebView2> weakThis (this);
  514. webViewEnvironment->CreateCoreWebView2Controller ((HWND) peer->getNativeHandle(),
  515. Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler> (
  516. [this, weakThis] (HRESULT, ICoreWebView2Controller* controller) -> HRESULT
  517. {
  518. if (weakThis != nullptr)
  519. {
  520. isCreating = false;
  521. if (controller != nullptr)
  522. {
  523. webViewController = controller;
  524. controller->get_CoreWebView2 (webView.resetAndGetPointerAddress());
  525. addEventHandlers();
  526. componentMovedOrResized (true, true);
  527. if (webView != nullptr && urlRequest.url.isNotEmpty())
  528. webView->Navigate (urlRequest.url.toWideCharPointer());
  529. }
  530. }
  531. return S_OK;
  532. }).Get());
  533. }
  534. }
  535. void closeWebView()
  536. {
  537. if (webViewController.get() != nullptr)
  538. {
  539. webViewController->Close();
  540. webViewController = nullptr;
  541. webView = nullptr;
  542. }
  543. webViewEnvironment = nullptr;
  544. }
  545. //==============================================================================
  546. void setControlBounds (Rectangle<int> newBounds) const
  547. {
  548. if (webViewController != nullptr)
  549. {
  550. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  551. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  552. newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
  553. #endif
  554. webViewController->put_Bounds({ newBounds.getX(), newBounds.getY(),
  555. newBounds.getRight(), newBounds.getBottom() });
  556. }
  557. }
  558. void setControlVisible (bool shouldBeVisible) const
  559. {
  560. if (webViewController != nullptr)
  561. webViewController->put_IsVisible (shouldBeVisible);
  562. }
  563. //==============================================================================
  564. WebBrowserComponent& owner;
  565. HMODULE webView2LoaderHandle = nullptr;
  566. WinRTWrapper::ComPtr<ICoreWebView2Environment> webViewEnvironment;
  567. WinRTWrapper::ComPtr<ICoreWebView2Controller> webViewController;
  568. WinRTWrapper::ComPtr<ICoreWebView2> webView;
  569. EventRegistrationToken navigationStartingToken { 0 },
  570. newWindowRequestedToken { 0 },
  571. windowCloseRequestedToken { 0 },
  572. navigationCompletedToken { 0 },
  573. webResourceRequestedToken { 0 };
  574. struct URLRequest
  575. {
  576. String url;
  577. StringArray headers;
  578. MemoryBlock postData;
  579. };
  580. URLRequest urlRequest;
  581. bool isCreating = false;
  582. //==============================================================================
  583. JUCE_DECLARE_WEAK_REFERENCEABLE (WebView2)
  584. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebView2)
  585. };
  586. #endif
  587. //==============================================================================
  588. class WebBrowserComponent::Pimpl
  589. {
  590. public:
  591. Pimpl (WebBrowserComponent& owner, const File& dllLocation, const File& userDataFolder, bool useWebView2)
  592. {
  593. if (useWebView2)
  594. {
  595. #if JUCE_USE_WIN_WEBVIEW2
  596. try
  597. {
  598. internal.reset (new WebView2 (owner, dllLocation, userDataFolder));
  599. }
  600. catch (std::runtime_error&) {}
  601. #endif
  602. }
  603. ignoreUnused (dllLocation, userDataFolder);
  604. if (internal == nullptr)
  605. internal.reset (new Win32WebView (owner));
  606. }
  607. InternalWebViewType& getInternalWebView()
  608. {
  609. return *internal;
  610. }
  611. private:
  612. std::unique_ptr<InternalWebViewType> internal;
  613. };
  614. //==============================================================================
  615. WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden)
  616. : browser (new Pimpl (*this, {}, {}, false)),
  617. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  618. {
  619. setOpaque (true);
  620. }
  621. WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden,
  622. const File& dllLocation,
  623. const File& userDataFolder)
  624. : browser (new Pimpl (*this, dllLocation, userDataFolder, true)),
  625. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  626. {
  627. setOpaque (true);
  628. }
  629. WebBrowserComponent::~WebBrowserComponent()
  630. {
  631. }
  632. //==============================================================================
  633. void WebBrowserComponent::goToURL (const String& url,
  634. const StringArray* headers,
  635. const MemoryBlock* postData)
  636. {
  637. lastURL = url;
  638. if (headers != nullptr)
  639. lastHeaders = *headers;
  640. else
  641. lastHeaders.clear();
  642. if (postData != nullptr)
  643. lastPostData = *postData;
  644. else
  645. lastPostData.reset();
  646. blankPageShown = false;
  647. if (! browser->getInternalWebView().hasBrowserBeenCreated())
  648. checkWindowAssociation();
  649. browser->getInternalWebView().goToURL (url, headers, postData);
  650. }
  651. void WebBrowserComponent::stop()
  652. {
  653. browser->getInternalWebView().stop();
  654. }
  655. void WebBrowserComponent::goBack()
  656. {
  657. lastURL.clear();
  658. blankPageShown = false;
  659. browser->getInternalWebView().goBack();
  660. }
  661. void WebBrowserComponent::goForward()
  662. {
  663. lastURL.clear();
  664. browser->getInternalWebView().goForward();
  665. }
  666. void WebBrowserComponent::refresh()
  667. {
  668. browser->getInternalWebView().refresh();
  669. }
  670. //==============================================================================
  671. void WebBrowserComponent::paint (Graphics& g)
  672. {
  673. if (! browser->getInternalWebView().hasBrowserBeenCreated())
  674. {
  675. g.fillAll (Colours::white);
  676. checkWindowAssociation();
  677. }
  678. }
  679. void WebBrowserComponent::checkWindowAssociation()
  680. {
  681. if (isShowing())
  682. {
  683. if (! browser->getInternalWebView().hasBrowserBeenCreated() && getPeer() != nullptr)
  684. {
  685. browser->getInternalWebView().createBrowser();
  686. reloadLastURL();
  687. }
  688. else
  689. {
  690. if (blankPageShown)
  691. goBack();
  692. }
  693. }
  694. else
  695. {
  696. if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
  697. {
  698. // when the component becomes invisible, some stuff like flash
  699. // carries on playing audio, so we need to force it onto a blank
  700. // page to avoid this..
  701. blankPageShown = true;
  702. browser->getInternalWebView().goToURL ("about:blank", 0, 0);
  703. }
  704. }
  705. }
  706. void WebBrowserComponent::reloadLastURL()
  707. {
  708. if (lastURL.isNotEmpty())
  709. {
  710. goToURL (lastURL, &lastHeaders, &lastPostData);
  711. lastURL.clear();
  712. }
  713. }
  714. void WebBrowserComponent::parentHierarchyChanged()
  715. {
  716. checkWindowAssociation();
  717. }
  718. void WebBrowserComponent::resized()
  719. {
  720. browser->getInternalWebView().setWebViewSize (getWidth(), getHeight());
  721. }
  722. void WebBrowserComponent::visibilityChanged()
  723. {
  724. checkWindowAssociation();
  725. }
  726. void WebBrowserComponent::focusGained (FocusChangeType)
  727. {
  728. browser->getInternalWebView().focusGained();
  729. }
  730. void WebBrowserComponent::clearCookies()
  731. {
  732. HeapBlock<::INTERNET_CACHE_ENTRY_INFOA> entry;
  733. ::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFOA);
  734. ::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  735. if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  736. {
  737. entry.realloc (1, entrySize);
  738. urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  739. }
  740. if (urlCacheHandle != nullptr)
  741. {
  742. for (;;)
  743. {
  744. ::DeleteUrlCacheEntryA (entry.getData()->lpszSourceUrlName);
  745. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) == 0)
  746. {
  747. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  748. {
  749. entry.realloc (1, entrySize);
  750. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) != 0)
  751. continue;
  752. }
  753. break;
  754. }
  755. }
  756. FindCloseUrlCache (urlCacheHandle);
  757. }
  758. }
  759. } // namespace juce