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.

957 lines
32KB

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