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.

942 lines
31KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. struct InternalWebViewType
  16. {
  17. InternalWebViewType() {}
  18. virtual ~InternalWebViewType() {}
  19. virtual void createBrowser() = 0;
  20. virtual bool hasBrowserBeenCreated() = 0;
  21. virtual void goToURL (const String&, const StringArray*, const MemoryBlock*) = 0;
  22. virtual void stop() = 0;
  23. virtual void goBack() = 0;
  24. virtual void goForward() = 0;
  25. virtual void refresh() = 0;
  26. virtual void focusGained() {}
  27. virtual void setWebViewSize (int, int) = 0;
  28. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternalWebViewType)
  29. };
  30. #if JUCE_MINGW
  31. JUCE_DECLARE_UUID_GETTER (IOleClientSite, "00000118-0000-0000-c000-000000000046")
  32. JUCE_DECLARE_UUID_GETTER (IDispatch, "00020400-0000-0000-c000-000000000046")
  33. #ifndef WebBrowser
  34. class WebBrowser;
  35. #endif
  36. #endif
  37. JUCE_DECLARE_UUID_GETTER (DWebBrowserEvents2, "34A715A0-6587-11D0-924A-0020AFC7AC4D")
  38. JUCE_DECLARE_UUID_GETTER (IConnectionPointContainer, "B196B284-BAB4-101A-B69C-00AA00341D07")
  39. JUCE_DECLARE_UUID_GETTER (IWebBrowser2, "D30C1661-CDAF-11D0-8A3E-00C04FC9E26E")
  40. JUCE_DECLARE_UUID_GETTER (WebBrowser, "8856F961-340A-11D0-A96B-00C04FD705A2")
  41. //==============================================================================
  42. class Win32WebView : public InternalWebViewType,
  43. public ActiveXControlComponent
  44. {
  45. public:
  46. Win32WebView (WebBrowserComponent& owner)
  47. {
  48. owner.addAndMakeVisible (this);
  49. }
  50. ~Win32WebView() override
  51. {
  52. if (connectionPoint != nullptr)
  53. connectionPoint->Unadvise (adviseCookie);
  54. if (browser != nullptr)
  55. browser->Release();
  56. }
  57. void createBrowser() override
  58. {
  59. auto webCLSID = __uuidof (WebBrowser);
  60. createControl (&webCLSID);
  61. auto iidWebBrowser2 = __uuidof (IWebBrowser2);
  62. auto iidConnectionPointContainer = __uuidof (IConnectionPointContainer);
  63. browser = (IWebBrowser2*) queryInterface (&iidWebBrowser2);
  64. if (auto connectionPointContainer = (IConnectionPointContainer*) queryInterface (&iidConnectionPointContainer))
  65. {
  66. connectionPointContainer->FindConnectionPoint (__uuidof (DWebBrowserEvents2), &connectionPoint);
  67. if (connectionPoint != nullptr)
  68. {
  69. auto* owner = dynamic_cast<WebBrowserComponent*> (Component::getParentComponent());
  70. jassert (owner != nullptr);
  71. auto handler = new EventHandler (*owner);
  72. connectionPoint->Advise (handler, &adviseCookie);
  73. handler->Release();
  74. }
  75. }
  76. }
  77. bool hasBrowserBeenCreated() override
  78. {
  79. return browser != nullptr;
  80. }
  81. void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
  82. {
  83. if (browser != nullptr)
  84. {
  85. VARIANT headerFlags, frame, postDataVar, headersVar; // (_variant_t isn't available in all compilers)
  86. VariantInit (&headerFlags);
  87. VariantInit (&frame);
  88. VariantInit (&postDataVar);
  89. VariantInit (&headersVar);
  90. if (headers != nullptr)
  91. {
  92. V_VT (&headersVar) = VT_BSTR;
  93. V_BSTR (&headersVar) = SysAllocString ((const OLECHAR*) headers->joinIntoString ("\r\n").toWideCharPointer());
  94. }
  95. if (postData != nullptr && postData->getSize() > 0)
  96. {
  97. auto sa = SafeArrayCreateVector (VT_UI1, 0, (ULONG) postData->getSize());
  98. if (sa != nullptr)
  99. {
  100. void* data = nullptr;
  101. SafeArrayAccessData (sa, &data);
  102. jassert (data != nullptr);
  103. if (data != nullptr)
  104. {
  105. postData->copyTo (data, 0, postData->getSize());
  106. SafeArrayUnaccessData (sa);
  107. VARIANT postDataVar2;
  108. VariantInit (&postDataVar2);
  109. V_VT (&postDataVar2) = VT_ARRAY | VT_UI1;
  110. V_ARRAY (&postDataVar2) = sa;
  111. sa = nullptr;
  112. postDataVar = postDataVar2;
  113. }
  114. else
  115. {
  116. SafeArrayDestroy (sa);
  117. }
  118. }
  119. }
  120. auto urlBSTR = SysAllocString ((const OLECHAR*) url.toWideCharPointer());
  121. browser->Navigate (urlBSTR, &headerFlags, &frame, &postDataVar, &headersVar);
  122. SysFreeString (urlBSTR);
  123. VariantClear (&headerFlags);
  124. VariantClear (&frame);
  125. VariantClear (&postDataVar);
  126. VariantClear (&headersVar);
  127. }
  128. }
  129. void stop() override
  130. {
  131. if (browser != nullptr)
  132. browser->Stop();
  133. }
  134. void goBack() override
  135. {
  136. if (browser != nullptr)
  137. browser->GoBack();
  138. }
  139. void goForward() override
  140. {
  141. if (browser != nullptr)
  142. browser->GoForward();
  143. }
  144. void refresh() override
  145. {
  146. if (browser != nullptr)
  147. browser->Refresh();
  148. }
  149. void focusGained() override
  150. {
  151. auto iidOleObject = __uuidof (IOleObject);
  152. auto iidOleWindow = __uuidof (IOleWindow);
  153. if (auto oleObject = (IOleObject*) queryInterface (&iidOleObject))
  154. {
  155. if (auto oleWindow = (IOleWindow*) queryInterface (&iidOleWindow))
  156. {
  157. IOleClientSite* oleClientSite = nullptr;
  158. if (SUCCEEDED (oleObject->GetClientSite (&oleClientSite)))
  159. {
  160. HWND hwnd;
  161. oleWindow->GetWindow (&hwnd);
  162. oleObject->DoVerb (OLEIVERB_UIACTIVATE, nullptr, oleClientSite, 0, hwnd, nullptr);
  163. oleClientSite->Release();
  164. }
  165. oleWindow->Release();
  166. }
  167. oleObject->Release();
  168. }
  169. }
  170. using ActiveXControlComponent::focusGained;
  171. void setWebViewSize (int width, int height) override
  172. {
  173. setSize (width, height);
  174. }
  175. private:
  176. IWebBrowser2* browser = nullptr;
  177. IConnectionPoint* connectionPoint = nullptr;
  178. DWORD adviseCookie = 0;
  179. //==============================================================================
  180. struct EventHandler : public ComBaseClassHelper<IDispatch>,
  181. public ComponentMovementWatcher
  182. {
  183. EventHandler (WebBrowserComponent& w) : ComponentMovementWatcher (&w), owner (w) {}
  184. JUCE_COMRESULT GetTypeInfoCount (UINT*) override { return E_NOTIMPL; }
  185. JUCE_COMRESULT GetTypeInfo (UINT, LCID, ITypeInfo**) override { return E_NOTIMPL; }
  186. JUCE_COMRESULT GetIDsOfNames (REFIID, LPOLESTR*, UINT, LCID, DISPID*) override { return E_NOTIMPL; }
  187. JUCE_COMRESULT Invoke (DISPID dispIdMember, REFIID /*riid*/, LCID /*lcid*/, WORD /*wFlags*/, DISPPARAMS* pDispParams,
  188. VARIANT* /*pVarResult*/, EXCEPINFO* /*pExcepInfo*/, UINT* /*puArgErr*/) override
  189. {
  190. if (dispIdMember == DISPID_BEFORENAVIGATE2)
  191. {
  192. *pDispParams->rgvarg->pboolVal
  193. = owner.pageAboutToLoad (getStringFromVariant (pDispParams->rgvarg[5].pvarVal)) ? VARIANT_FALSE
  194. : VARIANT_TRUE;
  195. return S_OK;
  196. }
  197. if (dispIdMember == 273 /*DISPID_NEWWINDOW3*/)
  198. {
  199. owner.newWindowAttemptingToLoad (pDispParams->rgvarg[0].bstrVal);
  200. *pDispParams->rgvarg[3].pboolVal = VARIANT_TRUE;
  201. return S_OK;
  202. }
  203. if (dispIdMember == DISPID_DOCUMENTCOMPLETE)
  204. {
  205. owner.pageFinishedLoading (getStringFromVariant (pDispParams->rgvarg[0].pvarVal));
  206. return S_OK;
  207. }
  208. if (dispIdMember == 271 /*DISPID_NAVIGATEERROR*/)
  209. {
  210. int statusCode = pDispParams->rgvarg[1].pvarVal->intVal;
  211. *pDispParams->rgvarg[0].pboolVal = VARIANT_FALSE;
  212. // IWebBrowser2 also reports http status codes here, we need
  213. // report only network errors
  214. if (statusCode < 0)
  215. {
  216. LPTSTR messageBuffer = nullptr;
  217. auto size = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  218. nullptr, statusCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  219. (LPTSTR) &messageBuffer, 0, nullptr);
  220. String message (messageBuffer, size);
  221. LocalFree (messageBuffer);
  222. if (! owner.pageLoadHadNetworkError (message))
  223. *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
  224. }
  225. return S_OK;
  226. }
  227. if (dispIdMember == 263 /*DISPID_WINDOWCLOSING*/)
  228. {
  229. owner.windowCloseRequest();
  230. // setting this bool tells the browser to ignore the event - we'll handle it.
  231. if (pDispParams->cArgs > 0 && pDispParams->rgvarg[0].vt == (VT_BYREF | VT_BOOL))
  232. *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE;
  233. return S_OK;
  234. }
  235. return E_NOTIMPL;
  236. }
  237. void componentMovedOrResized (bool, bool) override {}
  238. void componentPeerChanged() override {}
  239. void componentVisibilityChanged() override { owner.visibilityChanged(); }
  240. private:
  241. WebBrowserComponent& owner;
  242. static String getStringFromVariant (VARIANT* v)
  243. {
  244. return (v->vt & VT_BYREF) != 0 ? *v->pbstrVal
  245. : v->bstrVal;
  246. }
  247. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler)
  248. };
  249. //==============================================================================
  250. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32WebView)
  251. };
  252. #if JUCE_USE_WIN_WEBVIEW2
  253. using namespace Microsoft::WRL;
  254. class WebView2 : public InternalWebViewType,
  255. public Component,
  256. public ComponentMovementWatcher
  257. {
  258. public:
  259. WebView2 (WebBrowserComponent& o, const File& dllLocation, const File& userDataFolder)
  260. : ComponentMovementWatcher (&o),
  261. owner (o)
  262. {
  263. if (! WinRTWrapper::getInstance()->isInitialised())
  264. throw std::runtime_error ("Failed to initialise the WinRT wrapper");
  265. if (! createWebViewEnvironment (dllLocation, userDataFolder))
  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. WinRTWrapper::ComPtr<ICoreWebView2WebResourceRequest> request;
  421. args->get_Request (request.resetAndGetPointerAddress());
  422. auto uriString = getUriStringFromArgs (request.get());
  423. if (uriString == urlRequest.url
  424. || (uriString.endsWith ("/") && uriString.upToLastOccurrenceOf ("/", false, false) == urlRequest.url))
  425. {
  426. String method ("GET");
  427. if (urlRequest.postData.getSize() > 0)
  428. {
  429. method = "POST";
  430. WinRTWrapper::ComPtr<IStream> content (SHCreateMemStream ((BYTE*) urlRequest.postData.getData(),
  431. (UINT) urlRequest.postData.getSize()));
  432. request->put_Content (content.get());
  433. }
  434. if (! urlRequest.headers.isEmpty())
  435. {
  436. WinRTWrapper::ComPtr<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. bool createWebViewEnvironment (const File& dllLocation, const File& userDataFolder)
  471. {
  472. using CreateWebViewEnvironmentWithOptionsFunc = HRESULT (*) (PCWSTR, PCWSTR,
  473. ICoreWebView2EnvironmentOptions*,
  474. ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler*);
  475. auto dllPath = dllLocation.getFullPathName();
  476. if (dllPath.isEmpty())
  477. dllPath = "WebView2Loader.dll";
  478. webView2LoaderHandle = LoadLibraryA (dllPath.toUTF8());
  479. if (webView2LoaderHandle == nullptr)
  480. return false;
  481. auto* createWebViewEnvironmentWithOptions = (CreateWebViewEnvironmentWithOptionsFunc) GetProcAddress (webView2LoaderHandle,
  482. "CreateCoreWebView2EnvironmentWithOptions");
  483. if (createWebViewEnvironmentWithOptions == nullptr)
  484. {
  485. // failed to load WebView2Loader.dll
  486. jassertfalse;
  487. return false;
  488. }
  489. auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
  490. WeakReference<WebView2> weakThis (this);
  491. auto hr = createWebViewEnvironmentWithOptions (nullptr,
  492. userDataFolder != File() ? userDataFolder.getFullPathName().toWideCharPointer() : nullptr,
  493. options.Get(),
  494. Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
  495. [this, weakThis] (HRESULT, ICoreWebView2Environment* env) -> HRESULT
  496. {
  497. if (weakThis != nullptr)
  498. webViewEnvironment = env;
  499. return S_OK;
  500. }).Get());
  501. return SUCCEEDED (hr);
  502. }
  503. void createWebView()
  504. {
  505. if (auto* peer = getPeer())
  506. {
  507. isCreating = true;
  508. WeakReference<WebView2> weakThis (this);
  509. webViewEnvironment->CreateCoreWebView2Controller ((HWND) peer->getNativeHandle(),
  510. Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler> (
  511. [this, weakThis] (HRESULT, ICoreWebView2Controller* controller) -> HRESULT
  512. {
  513. if (weakThis != nullptr)
  514. {
  515. isCreating = false;
  516. if (controller != nullptr)
  517. {
  518. webViewController = controller;
  519. controller->get_CoreWebView2 (webView.resetAndGetPointerAddress());
  520. addEventHandlers();
  521. componentMovedOrResized (true, true);
  522. if (webView != nullptr && urlRequest.url.isNotEmpty())
  523. webView->Navigate (urlRequest.url.toWideCharPointer());
  524. }
  525. }
  526. return S_OK;
  527. }).Get());
  528. }
  529. }
  530. void closeWebView()
  531. {
  532. if (webViewController.get() != nullptr)
  533. {
  534. webViewController->Close();
  535. webViewController = nullptr;
  536. webView = nullptr;
  537. }
  538. webViewEnvironment = nullptr;
  539. }
  540. //==============================================================================
  541. void setControlBounds (Rectangle<int> newBounds) const
  542. {
  543. if (webViewController != nullptr)
  544. {
  545. #if JUCE_WIN_PER_MONITOR_DPI_AWARE
  546. if (auto* peer = owner.getTopLevelComponent()->getPeer())
  547. newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
  548. #endif
  549. webViewController->put_Bounds({ newBounds.getX(), newBounds.getY(),
  550. newBounds.getRight(), newBounds.getBottom() });
  551. }
  552. }
  553. void setControlVisible (bool shouldBeVisible) const
  554. {
  555. if (webViewController != nullptr)
  556. webViewController->put_IsVisible (shouldBeVisible);
  557. }
  558. //==============================================================================
  559. WebBrowserComponent& owner;
  560. HMODULE webView2LoaderHandle = nullptr;
  561. WinRTWrapper::ComPtr<ICoreWebView2Environment> webViewEnvironment;
  562. WinRTWrapper::ComPtr<ICoreWebView2Controller> webViewController;
  563. WinRTWrapper::ComPtr<ICoreWebView2> webView;
  564. EventRegistrationToken navigationStartingToken { 0 },
  565. newWindowRequestedToken { 0 },
  566. windowCloseRequestedToken { 0 },
  567. navigationCompletedToken { 0 },
  568. webResourceRequestedToken { 0 };
  569. struct URLRequest
  570. {
  571. String url;
  572. StringArray headers;
  573. MemoryBlock postData;
  574. };
  575. URLRequest urlRequest;
  576. bool isCreating = false;
  577. //==============================================================================
  578. JUCE_DECLARE_WEAK_REFERENCEABLE (WebView2)
  579. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebView2)
  580. };
  581. #endif
  582. //==============================================================================
  583. class WebBrowserComponent::Pimpl
  584. {
  585. public:
  586. Pimpl (WebBrowserComponent& owner, const File& dllLocation, const File& userDataFolder, bool useWebView2)
  587. {
  588. if (useWebView2)
  589. {
  590. #if JUCE_USE_WIN_WEBVIEW2
  591. try
  592. {
  593. internal.reset (new WebView2 (owner, dllLocation, userDataFolder));
  594. }
  595. catch (std::runtime_error&) {}
  596. #endif
  597. }
  598. ignoreUnused (dllLocation, userDataFolder);
  599. if (internal == nullptr)
  600. internal.reset (new Win32WebView (owner));
  601. }
  602. InternalWebViewType& getInternalWebView()
  603. {
  604. return *internal;
  605. }
  606. private:
  607. std::unique_ptr<InternalWebViewType> internal;
  608. };
  609. //==============================================================================
  610. WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden)
  611. : browser (new Pimpl (*this, {}, {}, false)),
  612. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  613. {
  614. setOpaque (true);
  615. }
  616. WebBrowserComponent::WebBrowserComponent (bool unloadWhenHidden,
  617. const File& dllLocation,
  618. const File& userDataFolder)
  619. : browser (new Pimpl (*this, dllLocation, userDataFolder, true)),
  620. unloadPageWhenBrowserIsHidden (unloadWhenHidden)
  621. {
  622. setOpaque (true);
  623. }
  624. WebBrowserComponent::~WebBrowserComponent()
  625. {
  626. }
  627. //==============================================================================
  628. void WebBrowserComponent::goToURL (const String& url,
  629. const StringArray* headers,
  630. const MemoryBlock* postData)
  631. {
  632. lastURL = url;
  633. if (headers != nullptr)
  634. lastHeaders = *headers;
  635. else
  636. lastHeaders.clear();
  637. if (postData != nullptr)
  638. lastPostData = *postData;
  639. else
  640. lastPostData.reset();
  641. blankPageShown = false;
  642. if (! browser->getInternalWebView().hasBrowserBeenCreated())
  643. checkWindowAssociation();
  644. browser->getInternalWebView().goToURL (url, headers, postData);
  645. }
  646. void WebBrowserComponent::stop()
  647. {
  648. browser->getInternalWebView().stop();
  649. }
  650. void WebBrowserComponent::goBack()
  651. {
  652. lastURL.clear();
  653. blankPageShown = false;
  654. browser->getInternalWebView().goBack();
  655. }
  656. void WebBrowserComponent::goForward()
  657. {
  658. lastURL.clear();
  659. browser->getInternalWebView().goForward();
  660. }
  661. void WebBrowserComponent::refresh()
  662. {
  663. browser->getInternalWebView().refresh();
  664. }
  665. //==============================================================================
  666. void WebBrowserComponent::paint (Graphics& g)
  667. {
  668. if (! browser->getInternalWebView().hasBrowserBeenCreated())
  669. {
  670. g.fillAll (Colours::white);
  671. checkWindowAssociation();
  672. }
  673. }
  674. void WebBrowserComponent::checkWindowAssociation()
  675. {
  676. if (isShowing())
  677. {
  678. if (! browser->getInternalWebView().hasBrowserBeenCreated() && getPeer() != nullptr)
  679. {
  680. browser->getInternalWebView().createBrowser();
  681. reloadLastURL();
  682. }
  683. else
  684. {
  685. if (blankPageShown)
  686. goBack();
  687. }
  688. }
  689. else
  690. {
  691. if (browser != nullptr && unloadPageWhenBrowserIsHidden && ! blankPageShown)
  692. {
  693. // when the component becomes invisible, some stuff like flash
  694. // carries on playing audio, so we need to force it onto a blank
  695. // page to avoid this..
  696. blankPageShown = true;
  697. browser->getInternalWebView().goToURL ("about:blank", 0, 0);
  698. }
  699. }
  700. }
  701. void WebBrowserComponent::reloadLastURL()
  702. {
  703. if (lastURL.isNotEmpty())
  704. {
  705. goToURL (lastURL, &lastHeaders, &lastPostData);
  706. lastURL.clear();
  707. }
  708. }
  709. void WebBrowserComponent::parentHierarchyChanged()
  710. {
  711. checkWindowAssociation();
  712. }
  713. void WebBrowserComponent::resized()
  714. {
  715. browser->getInternalWebView().setWebViewSize (getWidth(), getHeight());
  716. }
  717. void WebBrowserComponent::visibilityChanged()
  718. {
  719. checkWindowAssociation();
  720. }
  721. void WebBrowserComponent::focusGained (FocusChangeType)
  722. {
  723. browser->getInternalWebView().focusGained();
  724. }
  725. void WebBrowserComponent::clearCookies()
  726. {
  727. HeapBlock<::INTERNET_CACHE_ENTRY_INFOA> entry;
  728. ::DWORD entrySize = sizeof (::INTERNET_CACHE_ENTRY_INFOA);
  729. ::HANDLE urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  730. if (urlCacheHandle == nullptr && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  731. {
  732. entry.realloc (1, entrySize);
  733. urlCacheHandle = ::FindFirstUrlCacheEntryA ("cookie:", entry.getData(), &entrySize);
  734. }
  735. if (urlCacheHandle != nullptr)
  736. {
  737. for (;;)
  738. {
  739. ::DeleteUrlCacheEntryA (entry.getData()->lpszSourceUrlName);
  740. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) == 0)
  741. {
  742. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  743. {
  744. entry.realloc (1, entrySize);
  745. if (::FindNextUrlCacheEntryA (urlCacheHandle, entry.getData(), &entrySize) != 0)
  746. continue;
  747. }
  748. break;
  749. }
  750. }
  751. FindCloseUrlCache (urlCacheHandle);
  752. }
  753. }
  754. } // namespace juce