1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Windows: Added support for Chromium-based WebView2 browser in WebBrowserComponent and removed WinRT webview

This commit is contained in:
ed 2020-06-12 14:21:01 +01:00
parent 5467c57e23
commit 87fcf2f353
7 changed files with 308 additions and 378 deletions

View file

@ -6,6 +6,7 @@
- Plugin projects require CMake 3.15 or higher.
- All iOS targets require CMake 3.14 or higher (3.15 or higher for plugins targeting iOS).
- Android targets are not currently supported.
- WebView2 on Windows via JUCE_USE_WIN_WEBVIEW2 flag in juce_gui_extra is not currently supported.
Most system package managers have packages for CMake, but we recommend using the most recent release
from https://cmake.org/download. You should always use a CMake that's newer than your build
@ -15,11 +16,11 @@ In addition to CMake you'll need a build toolchain for your platform, such as Xc
## Getting Started
In this directory, you'll find example projects for a GUI app, a console app, and an audio plugin.
You can simply copy one of these subdirectories out of the JUCE repo, add JUCE as a submodule, and
uncomment the call to `add_subdirectory` where indicated in the CMakeLists.txt. Alternatively, if
you've installed JUCE using a package manager or the CMake install target, you can uncomment the
call to `find_package`.
In the JUCE/examples/CMake directory, you'll find example projects for a GUI app, a console app,
and an audio plugin. You can simply copy one of these subdirectories out of the JUCE repo, add JUCE
as a submodule, and uncomment the call to `add_subdirectory` where indicated in the CMakeLists.txt.
Alternatively, if you've installed JUCE using a package manager or the CMake install target, you can
uncomment the call to `find_package`.
Once your project is set up, you can generate a build tree for it in the normal way. To get started,
you might invoke CMake like this, from the new directory you created.

View file

@ -53,27 +53,10 @@ public:
"Specifies the version of the platform toolset that will be used when building this project.");
}
void addIPPLibraryProperty (PropertyListBuilder& props)
{
props.add (new ChoicePropertyComponent (IPPLibraryValue, "Use IPP Library",
{ "No", "Yes (Default Linking)", "Multi-Threaded Static Library", "Single-Threaded Static Library", "Multi-Threaded DLL", "Single-Threaded DLL" },
{ var(), "true", "Parallel_Static", "Sequential", "Parallel_Dynamic", "Sequential_Dynamic" }),
"Enable this to use Intel's Integrated Performance Primitives library.");
}
void addWindowsTargetPlatformProperties (PropertyListBuilder& props)
{
auto isWindows10SDK = getVisualStudioVersion() > 14;
props.add (new TextPropertyComponent (targetPlatformVersion, "Windows Target Platform", 20, false),
String ("Specifies the version of the Windows SDK that will be used when building this project. ")
+ (isWindows10SDK ? "Leave this field empty to use the latest Windows 10 SDK installed on the build machine."
: "The default value for this exporter is " + getDefaultWindowsTargetPlatformVersion()));
}
void create (const OwnedArray<LibraryModule>&) const override
{
createResourcesAndIcon();
createPackagesConfigFile();
for (int i = 0; i < targets.size(); ++i)
if (auto* target = targets[i])
@ -720,6 +703,12 @@ public:
e->setAttribute ("Include", prependDot (getOwner().iconFile.getFileName()));
}
if (getOwner().packagesConfigFile.existsAsFile())
{
auto* e = otherFilesGroup->createNewChildElement ("None");
e->setAttribute ("Include", getOwner().packagesConfigFile.getFileName());
}
if (otherFilesGroup->getFirstChildElement() != nullptr)
projectXml.addChildElement (otherFilesGroup.release());
@ -736,8 +725,18 @@ public:
}
{
auto* e = projectXml.createNewChildElement ("ImportGroup");
e->setAttribute ("Label", "ExtensionTargets");
auto* importGroup = projectXml.createNewChildElement ("ImportGroup");
importGroup->setAttribute ("Label", "ExtensionTargets");
if (owner.shouldAddWebView2Package())
{
auto packageTargetsPath = "packages\\" + getWebView2PackageName() + "." + getWebView2PackageVersion()
+ "\\build\\native\\" + getWebView2PackageName() + ".targets";
auto* e = importGroup->createNewChildElement ("Import");
e->setAttribute ("Project", packageTargetsPath);
e->setAttribute ("Condition", "Exists('" + packageTargetsPath + "')");
}
}
}
@ -892,6 +891,12 @@ public:
e->createNewChildElement ("Filter")->addTextElement (ProjectSaver::getJuceCodeGroupName());
}
if (getOwner().packagesConfigFile.existsAsFile())
{
auto* e = otherFilesGroup->createNewChildElement ("None");
e->setAttribute ("Include", getOwner().packagesConfigFile.getFileName());
}
if (otherFilesGroup->getFirstChildElement() != nullptr)
filterXml.addChildElement (otherFilesGroup.release());
@ -1375,6 +1380,20 @@ public:
{
props.add (new TextPropertyComponent (manifestFileValue, "Manifest file", 8192, false),
"Path to a manifest input file which should be linked into your binary (path is relative to jucer file).");
props.add (new ChoicePropertyComponent (IPPLibraryValue, "Use IPP Library",
{ "No", "Yes (Default Linking)", "Multi-Threaded Static Library", "Single-Threaded Static Library", "Multi-Threaded DLL", "Single-Threaded DLL" },
{ var(), "true", "Parallel_Static", "Sequential", "Parallel_Dynamic", "Sequential_Dynamic" }),
"Enable this to use Intel's Integrated Performance Primitives library.");
{
auto isWindows10SDK = getVisualStudioVersion() > 14;
props.add (new TextPropertyComponent (targetPlatformVersion, "Windows Target Platform", 20, false),
String ("Specifies the version of the Windows SDK that will be used when building this project. ")
+ (isWindows10SDK ? "Leave this field empty to use the latest Windows 10 SDK installed on the build machine."
: "The default value for this exporter is " + getDefaultWindowsTargetPlatformVersion()));
}
}
enum OptimisationLevel
@ -1447,7 +1466,7 @@ private:
protected:
//==============================================================================
mutable File rcFile, iconFile;
mutable File rcFile, iconFile, packagesConfigFile;
OwnedArray<MSVCTargetBase> targets;
ValueWithDefault IPPLibraryValue, platformToolsetValue, targetPlatformVersion, manifestFileValue;
@ -1586,6 +1605,35 @@ protected:
}
}
bool shouldAddWebView2Package() const
{
return project.getEnabledModules().isModuleEnabled ("juce_gui_extra")
&& project.isConfigFlagEnabled ("JUCE_USE_WIN_WEBVIEW2", false);
}
static String getWebView2PackageName() { return "Microsoft.Web.WebView2"; }
static String getWebView2PackageVersion() { return "0.9.488"; }
void createPackagesConfigFile() const
{
if (shouldAddWebView2Package())
{
packagesConfigFile = getTargetFolder().getChildFile ("packages.config");
build_tools::writeStreamToFile (packagesConfigFile, [] (MemoryOutputStream& mo)
{
mo.setNewLineString ("\r\n");
mo << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << newLine
<< "<packages>" << newLine
<< "\t" << "<package id=" << getWebView2PackageName().quoted()
<< " version=" << getWebView2PackageVersion().quoted()
<< " />" << newLine
<< "</packages>" << newLine;
});
}
}
static String prependDot (const String& filename)
{
return build_tools::isAbsolutePath (filename) ? filename
@ -1643,15 +1691,11 @@ public:
void createExporterProperties (PropertyListBuilder& props) override
{
MSVCProjectExporterBase::createExporterProperties (props);
static const char* toolsetNames[] = { "v140", "v140_xp", "CTP_Nov2013" };
const var toolsets[] = { "v140", "v140_xp", "CTP_Nov2013" };
addToolsetProperty (props, toolsetNames, toolsets, numElementsInArray (toolsets));
addIPPLibraryProperty (props);
addWindowsTargetPlatformProperties (props);
MSVCProjectExporterBase::createExporterProperties (props);
}
JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterVC2015)
@ -1690,15 +1734,11 @@ public:
void createExporterProperties (PropertyListBuilder& props) override
{
MSVCProjectExporterBase::createExporterProperties (props);
static const char* toolsetNames[] = { "v140", "v140_xp", "v141", "v141_xp" };
const var toolsets[] = { "v140", "v140_xp", "v141", "v141_xp" };
addToolsetProperty (props, toolsetNames, toolsets, numElementsInArray (toolsets));
addIPPLibraryProperty (props);
addWindowsTargetPlatformProperties (props);
MSVCProjectExporterBase::createExporterProperties (props);
}
JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterVC2017)
@ -1737,15 +1777,11 @@ public:
void createExporterProperties (PropertyListBuilder& props) override
{
MSVCProjectExporterBase::createExporterProperties (props);
static const char* toolsetNames[] = { "v140", "v140_xp", "v141", "v141_xp", "v142" };
const var toolsets[] = { "v140", "v140_xp", "v141", "v141_xp", "v142" };
addToolsetProperty (props, toolsetNames, toolsets, numElementsInArray (toolsets));
addIPPLibraryProperty (props);
addWindowsTargetPlatformProperties (props);
MSVCProjectExporterBase::createExporterProperties (props);
}
JUCE_DECLARE_NON_COPYABLE (MSVCProjectExporterVC2019)

View file

@ -35,7 +35,7 @@
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
#if JUCE_USE_WINRT_MIDI || JUCE_USE_WINRT_WEBVIEW
#if JUCE_USE_WINRT_MIDI || JUCE_USE_WIN_WEBVIEW2
#define JUCE_EVENTS_INCLUDE_WINRT_WRAPPER 1
#endif

View file

@ -33,7 +33,7 @@
#define JUCE_GRAPHICS_INCLUDE_COREGRAPHICS_HELPERS 1
#define JUCE_GUI_BASICS_INCLUDE_XHEADERS 1
#if JUCE_USE_WINRT_WEBVIEW
#if JUCE_USE_WIN_WEBVIEW2
#define JUCE_EVENTS_INCLUDE_WINRT_WRAPPER 1
#endif
@ -85,9 +85,8 @@
#if JUCE_WEB_BROWSER
#include <exdisp.h>
#include <exdispid.h>
#if JUCE_USE_WINRT_WEBVIEW
#include <windows.web.ui.h>
#include <windows.web.ui.interop.h>
#if JUCE_USE_WIN_WEBVIEW2
#include <windows.foundation.h>
#include <windows.foundation.collections.h>
@ -96,7 +95,15 @@
#include <wrl.h>
#include <wrl/wrappers/corewrappers.h>
#pragma warning (pop)
#include "WebView2.h"
#pragma warning (push)
#pragma warning (disable: 4458)
#include "WebView2EnvironmentOptions.h"
#pragma warning (pop)
#endif
#endif
//==============================================================================

View file

@ -57,19 +57,20 @@
#define JUCE_WEB_BROWSER 1
#endif
/** Config: JUCE_USE_WINRT_WEBVIEW
Enables the use of the EdgeHTML browser engine on Windows. This will use
the Windows Runtime API on Windows 10 version 1809 (October 2018 Update)
and later. If you enable this flag then older versions of Windows will
automatically fall back to using the regualar Win32 web view.
/** Config: JUCE_USE_WIN_WEBVIEW2
Enables the use of the Microsoft Edge (Chromium) WebView2 browser on Windows,
currently in developer preview. This requires Microsoft Edge (minimum version
82.0.488.0) to be installed on the user's machine at runtime.
You will need version 10.0.14393.0 of the Windows Standalone SDK to compile
and you may need to add the path to the WinRT headers. The path to the
headers will be something similar to
"C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
If using the Projucer, the Microsoft.Web.WebView2 package will be added to the
project solution if this flag is enabled. If you are building using CMake you
will need to manually add the package via the Visual Studio package manager.
If the required components are not available at runtime it will fall back to the
IE-based Win32 web view.
*/
#ifndef JUCE_USE_WINRT_WEBVIEW
#define JUCE_USE_WINRT_WEBVIEW 0
#ifndef JUCE_USE_WIN_WEBVIEW2
#define JUCE_USE_WIN_WEBVIEW2 0
#endif
/** Config: JUCE_ENABLE_LIVE_CONSTANT_EDITOR

View file

@ -26,9 +26,10 @@ namespace juce
A component that displays an embedded web browser.
The browser itself will be platform-dependent. On Mac and iOS it will be
WebKit, if you have enabled JUCE_USE_WINRT_WEBVIEW on Windows 10 it will be
EdgeHTML otherwise IE, on Android it will be Chrome, and on Linux it will be
WebKit.
WebKit, on Android it will be Chrome, and on Linux it will be WebKit. On
Windows, if the JUCE_USE_WIN_WEBVIEW2 flag is enabled, it will either be
Microsoft Edge (Chromium) or IE depending on availability of the Edge
runtime.
@tags{GUI}
*/

View file

@ -310,114 +310,97 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler)
};
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32WebView)
};
#if JUCE_USE_WINRT_WEBVIEW
extern RTL_OSVERSIONINFOW getWindowsVersionInfo();
#if JUCE_USE_WIN_WEBVIEW2
using namespace Microsoft::WRL;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Storage::Streams;
using namespace ABI::Windows::Web;
using namespace ABI::Windows::Web::UI;
using namespace ABI::Windows::Web::UI::Interop;
using namespace ABI::Windows::Web::Http;
using namespace ABI::Windows::Web::Http::Headers;
//==============================================================================
class WinRTWebView : public InternalWebViewType,
class WebView2 : public InternalWebViewType,
public Component,
public ComponentMovementWatcher
{
public:
WinRTWebView (WebBrowserComponent& o)
WebView2 (WebBrowserComponent& o)
: ComponentMovementWatcher (&o),
owner (o)
{
if (! WinRTWrapper::getInstance()->isInitialised())
throw std::runtime_error ("Failed to initialise the WinRT wrapper");
if (! createWebViewProcess())
throw std::runtime_error ("Failed to create the WebViewControlProcess");
if (! createWebViewEnvironment())
throw std::runtime_error ("Failed to create the CoreWebView2Environemnt");
owner.addAndMakeVisible (this);
}
~WinRTWebView() override
~WebView2() override
{
if (webViewControl != nullptr)
webViewControl->Stop();
removeEventHandlers();
webViewProcess->Terminate();
closeWebView();
}
void createBrowser() override
{
if (webViewControl == nullptr)
createWebViewControl();
if (webView == nullptr)
{
jassert (webViewEnvironment != nullptr);
createWebView();
}
}
bool hasBrowserBeenCreated() override
{
return webViewControl != nullptr || isCreating;
return webView != nullptr || isCreating;
}
void goToURL (const String& url, const StringArray* headers, const MemoryBlock* postData) override
{
if (webViewControl != nullptr)
{
if ((headers != nullptr && ! headers->isEmpty())
|| (postData != nullptr && postData->getSize() > 0))
{
auto requestMessage = createHttpRequestMessage (url, headers, postData);
webViewControl->NavigateWithHttpRequestMessage (requestMessage.get());
}
else
{
auto uri = createURI (url);
webViewControl->Navigate (uri.get());
}
}
urlRequest = { url,
headers != nullptr ? *headers : StringArray(),
postData != nullptr && postData->getSize() > 0 ? *postData : MemoryBlock() };
if (webView != nullptr)
webView->Navigate (urlRequest.url.toWideCharPointer());
}
void stop() override
{
if (webViewControl != nullptr)
webViewControl->Stop();
if (webView != nullptr)
webView->Stop();
}
void goBack() override
{
if (webViewControl != nullptr)
if (webView != nullptr)
{
boolean canGoBack = false;
webViewControl->get_CanGoBack (&canGoBack);
BOOL canGoBack = false;
webView->get_CanGoBack (&canGoBack);
if (canGoBack)
webViewControl->GoBack();
webView->GoBack();
}
}
void goForward() override
{
if (webViewControl != nullptr)
if (webView != nullptr)
{
boolean canGoForward = false;
webViewControl->get_CanGoForward (&canGoForward);
BOOL canGoForward = false;
webView->get_CanGoForward (&canGoForward);
if (canGoForward)
webViewControl->GoForward();
webView->GoForward();
}
}
void refresh() override
{
if (webViewControl != nullptr)
webViewControl->Refresh();
if (webView != nullptr)
webView->Reload();
}
void setWebViewSize (int width, int height) override
@ -446,117 +429,40 @@ public:
private:
//==============================================================================
template<typename OperationResultType, typename ResultsType>
static HRESULT waitForCompletion (IAsyncOperation<OperationResultType>* op, ResultsType* results)
template<class ArgType>
static String getUriStringFromArgs (ArgType* args)
{
using OperationType = IAsyncOperation<OperationResultType>;
using DelegateType = IAsyncOperationCompletedHandler<OperationResultType>;
struct EventDelegate : public RuntimeClass<RuntimeClassFlags<RuntimeClassType::Delegate>,
DelegateType,
FtmBase>
if (args != nullptr)
{
EventDelegate() = default;
LPWSTR uri;
args->get_Uri (&uri);
~EventDelegate()
{
CloseHandle (eventCompleted);
}
HRESULT RuntimeClassInitialize()
{
eventCompleted = CreateEventEx (nullptr, nullptr, 0, EVENT_ALL_ACCESS);
return eventCompleted == nullptr ? HRESULT_FROM_WIN32 (GetLastError()) : S_OK;
}
HRESULT Invoke (OperationType*, AsyncStatus newStatus)
{
status = newStatus;
SetEvent (eventCompleted);
return S_OK;
}
AsyncStatus status = AsyncStatus::Started;
HANDLE eventCompleted = nullptr;
};
WinRTWrapper::ComPtr<OperationType> operation = op;
WinRTWrapper::ComPtr<EventDelegate> eventCallback;
auto hr = MakeAndInitialize<EventDelegate> (eventCallback.resetAndGetPointerAddress());
if (SUCCEEDED (hr))
{
hr = operation->put_Completed (eventCallback.get());
if (SUCCEEDED (hr))
{
HANDLE waitForEvents[1] { eventCallback->eventCompleted };
auto handleCount = (ULONG) ARRAYSIZE (waitForEvents);
DWORD handleIndex = 0;
hr = CoWaitForMultipleHandles (COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE,
INFINITE, handleCount, waitForEvents, &handleIndex);
if (SUCCEEDED (hr))
{
if (eventCallback->status == AsyncStatus::Completed)
{
hr = operation->GetResults (results);
}
else
{
WinRTWrapper::ComPtr<IAsyncInfo> asyncInfo;
if (SUCCEEDED (operation->QueryInterface (asyncInfo.resetAndGetPointerAddress())))
asyncInfo->get_ErrorCode (&hr);
}
}
}
}
return hr;
}
//==============================================================================
template<class ArgsType>
String getURIStringFromArgs (ArgsType& args)
{
WinRTWrapper::ComPtr<IUriRuntimeClass> uri;
args.get_Uri (uri.resetAndGetPointerAddress());
if (uri != nullptr)
{
HSTRING uriString;
uri->get_AbsoluteUri (&uriString);
return WinRTWrapper::getInstance()->hStringToString (uriString);
return uri;
}
return {};
}
//==============================================================================
void addEventHandlers()
{
if (webViewControl != nullptr)
if (webView != nullptr)
{
webViewControl->add_NavigationStarting (Callback<ITypedEventHandler<IWebViewControl*, WebViewControlNavigationStartingEventArgs*>> (
[this] (IWebViewControl*, IWebViewControlNavigationStartingEventArgs* args)
webView->add_NavigationStarting (Callback<ICoreWebView2NavigationStartingEventHandler> (
[this] (ICoreWebView2*, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
{
auto uriString = getURIStringFromArgs (*args);
auto uriString = getUriStringFromArgs (args);
if (uriString.isNotEmpty())
args->put_Cancel (! owner.pageAboutToLoad (uriString));
if (uriString.isNotEmpty() && ! owner.pageAboutToLoad (uriString))
args->put_Cancel (true);
return S_OK;
}
).Get(), &navigationStartingToken);
}).Get(), &navigationStartingToken);
webViewControl->add_NewWindowRequested (Callback<ITypedEventHandler<IWebViewControl*, WebViewControlNewWindowRequestedEventArgs*>> (
[this] (IWebViewControl*, IWebViewControlNewWindowRequestedEventArgs* args)
webView->add_NewWindowRequested (Callback<ICoreWebView2NewWindowRequestedEventHandler> (
[this] (ICoreWebView2*, ICoreWebView2NewWindowRequestedEventArgs* args) -> HRESULT
{
auto uriString = getURIStringFromArgs (*args);
auto uriString = getUriStringFromArgs (args);
if (uriString.isNotEmpty())
{
@ -565,237 +471,220 @@ private:
}
return S_OK;
}
).Get(), &newWindowRequestedToken);
}).Get(), &newWindowRequestedToken);
webViewControl->add_NavigationCompleted (Callback<ITypedEventHandler<IWebViewControl*, WebViewControlNavigationCompletedEventArgs*>> (
[this] (IWebViewControl*, IWebViewControlNavigationCompletedEventArgs* args)
webView->add_WindowCloseRequested (Callback<ICoreWebView2WindowCloseRequestedEventHandler> (
[this] (ICoreWebView2*, IUnknown*) -> HRESULT
{
auto uriString = getURIStringFromArgs (*args);
owner.windowCloseRequest();
return S_OK;
}).Get(), &windowCloseRequestedToken);
webView->add_NavigationCompleted (Callback<ICoreWebView2NavigationCompletedEventHandler> (
[this] (ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT
{
LPWSTR uri;
sender->get_Source (&uri);
String uriString (uri);
if (uriString.isNotEmpty())
{
boolean success;
BOOL success = false;
args->get_IsSuccess (&success);
if (success)
COREWEBVIEW2_WEB_ERROR_STATUS errorStatus;
args->get_WebErrorStatus (&errorStatus);
if (success
|| errorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED) // this error seems to happen erroneously so ignore
{
owner.pageFinishedLoading (uriString);
}
else
{
WebErrorStatus status;
args->get_WebErrorStatus (&status);
auto errorString = "Error code: " + String (errorStatus);
owner.pageLoadHadNetworkError ("Error code: " + String (status));
if (owner.pageLoadHadNetworkError (errorString))
owner.goToURL ("data:text/plain;charset=UTF-8," + errorString);
}
}
return S_OK;
}).Get(), &navigationCompletedToken);
webView->AddWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
webView->add_WebResourceRequested (Callback<ICoreWebView2WebResourceRequestedEventHandler> (
[this] (ICoreWebView2*, ICoreWebView2WebResourceRequestedEventArgs* args) -> HRESULT
{
if (urlRequest.url.isEmpty())
return S_OK;
WinRTWrapper::ComPtr<ICoreWebView2WebResourceRequest> request;
args->get_Request (request.resetAndGetPointerAddress());
auto uriString = getUriStringFromArgs (request.get());
if (uriString == urlRequest.url
|| (uriString.endsWith ("/") && uriString.upToLastOccurrenceOf ("/", false, false) == urlRequest.url))
{
String method ("GET");
if (urlRequest.postData.getSize() > 0)
{
method = "POST";
WinRTWrapper::ComPtr<IStream> content (SHCreateMemStream ((BYTE*) urlRequest.postData.getData(),
(UINT) urlRequest.postData.getSize()));
request->put_Content (content.get());
}
).Get(), &navigationCompletedToken);
if (! urlRequest.headers.isEmpty())
{
WinRTWrapper::ComPtr<ICoreWebView2HttpRequestHeaders> headers;
request->get_Headers (headers.resetAndGetPointerAddress());
for (auto& header : urlRequest.headers)
{
headers->SetHeader (header.upToFirstOccurrenceOf (":", false, false).trim().toWideCharPointer(),
header.fromFirstOccurrenceOf (":", false, false).trim().toWideCharPointer());
}
}
request->put_Method (method.toWideCharPointer());
urlRequest = {};
}
return S_OK;
}).Get(), &webResourceRequestedToken);
}
}
void removeEventHandlers()
{
if (webViewControl != nullptr)
if (webView != nullptr)
{
if (navigationStartingToken.value != 0)
webViewControl->remove_NavigationStarting (navigationStartingToken);
webView->remove_NavigationStarting (navigationStartingToken);
if (newWindowRequestedToken.value != 0)
webViewControl->remove_NewWindowRequested (newWindowRequestedToken);
webView->remove_NewWindowRequested (newWindowRequestedToken);
if (windowCloseRequestedToken.value != 0)
webView->remove_WindowCloseRequested (windowCloseRequestedToken);
if (navigationCompletedToken.value != 0)
webViewControl->remove_NavigationCompleted (navigationCompletedToken);
}
}
webView->remove_NavigationCompleted (navigationCompletedToken);
bool createWebViewProcess()
if (webResourceRequestedToken.value != 0)
{
auto webViewControlProcessFactory
= WinRTWrapper::getInstance()->getWRLFactory<IWebViewControlProcessFactory> (RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcess);
if (webViewControlProcessFactory == nullptr)
{
jassertfalse;
return false;
webView->RemoveWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
webView->remove_WebResourceRequested (webResourceRequestedToken);
}
}
}
auto webViewProcessOptions
= WinRTWrapper::getInstance()->activateInstance<IWebViewControlProcessOptions> (RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcessOptions,
__uuidof (IWebViewControlProcessOptions));
bool createWebViewEnvironment()
{
auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
webViewProcessOptions->put_PrivateNetworkClientServerCapability (WebViewControlProcessCapabilityState_Enabled);
webViewControlProcessFactory->CreateWithOptions (webViewProcessOptions.get(), webViewProcess.resetAndGetPointerAddress());
auto hr = CreateCoreWebView2EnvironmentWithOptions (nullptr, nullptr, options.Get(),
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[this] (HRESULT, ICoreWebView2Environment* env) -> HRESULT
{
webViewEnvironment = env;
return S_OK;
}).Get());
return webViewProcess != nullptr;
return SUCCEEDED (hr);
}
void createWebViewControl()
void createWebView()
{
isCreating = true;
if (auto* peer = getPeer())
{
ScopedValueSetter<bool> svs (isCreating, true);
webViewEnvironment->CreateCoreWebView2Controller ((HWND) peer->getNativeHandle(),
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler> (
[this] (HRESULT, ICoreWebView2Controller* controller) -> HRESULT
{
webViewController = controller;
controller->get_CoreWebView2 (webView.resetAndGetPointerAddress());
WinRTWrapper::ComPtr<IAsyncOperation<WebViewControl*>> createWebViewAsyncOperation;
webViewProcess->CreateWebViewControlAsync ((INT64) peer->getNativeHandle(), {},
createWebViewAsyncOperation.resetAndGetPointerAddress());
waitForCompletion (createWebViewAsyncOperation.get(), webViewControl.resetAndGetPointerAddress());
isCreating = false;
addEventHandlers();
componentMovedOrResized (true, true);
if (webView != nullptr && urlRequest.url.isNotEmpty())
webView->Navigate (urlRequest.url.toWideCharPointer());
return S_OK;
}).Get());
}
}
//==============================================================================
WinRTWrapper::ComPtr<IUriRuntimeClass> createURI (const String& url)
void closeWebView()
{
auto uriRuntimeFactory
= WinRTWrapper::getInstance()->getWRLFactory <IUriRuntimeClassFactory> (RuntimeClass_Windows_Foundation_Uri);
if (uriRuntimeFactory == nullptr)
if (webViewController.get() != nullptr)
{
jassertfalse;
return {};
webViewController->Close();
webViewController = nullptr;
webView = nullptr;
}
WinRTWrapper::ScopedHString hstr (url);
WinRTWrapper::ComPtr<IUriRuntimeClass> uriRuntimeClass;
uriRuntimeFactory->CreateUri (hstr.get(), uriRuntimeClass.resetAndGetPointerAddress());
return uriRuntimeClass;
}
WinRTWrapper::ComPtr<IHttpContent> getPOSTContent (const MemoryBlock& postData)
{
auto factory = WinRTWrapper::getInstance()->getWRLFactory<IHttpStringContentFactory> (RuntimeClass_Windows_Web_Http_HttpStringContent);
if (factory == nullptr)
{
jassertfalse;
return {};
}
WinRTWrapper::ScopedHString hStr (postData.toString());
WinRTWrapper::ComPtr<IHttpContent> content;
factory->CreateFromString (hStr.get(), content.resetAndGetPointerAddress());
return content;
}
WinRTWrapper::ComPtr<IHttpMethod> getMethod (bool isPOST)
{
auto methodFactory = WinRTWrapper::getInstance()->getWRLFactory<IHttpMethodStatics> (RuntimeClass_Windows_Web_Http_HttpMethod);
if (methodFactory == nullptr)
{
jassertfalse;
return {};
}
WinRTWrapper::ComPtr<IHttpMethod> method;
if (isPOST)
methodFactory->get_Post (method.resetAndGetPointerAddress());
else
methodFactory->get_Get (method.resetAndGetPointerAddress());
return method;
}
void addHttpHeaders (WinRTWrapper::ComPtr<IHttpRequestMessage>& requestMessage, const StringArray& headers)
{
WinRTWrapper::ComPtr<IHttpRequestHeaderCollection> headerCollection;
requestMessage->get_Headers (headerCollection.resetAndGetPointerAddress());
for (int i = 0; i < headers.size(); ++i)
{
WinRTWrapper::ScopedHString headerName (headers[i].upToFirstOccurrenceOf (":", false, false).trim());
WinRTWrapper::ScopedHString headerValue (headers[i].fromFirstOccurrenceOf (":", false, false).trim());
headerCollection->Append (headerName.get(), headerValue.get());
}
}
WinRTWrapper::ComPtr<IHttpRequestMessage> createHttpRequestMessage (const String& url,
const StringArray* headers,
const MemoryBlock* postData)
{
auto requestFactory
= WinRTWrapper::getInstance()->getWRLFactory<IHttpRequestMessageFactory> (RuntimeClass_Windows_Web_Http_HttpRequestMessage);
if (requestFactory == nullptr)
{
jassertfalse;
return {};
}
bool isPOSTRequest = (postData != nullptr && postData->getSize() > 0);
auto method = getMethod (isPOSTRequest);
auto uri = createURI (url);
WinRTWrapper::ComPtr<IHttpRequestMessage> requestMessage;
requestFactory->Create (method.get(), uri.get(), requestMessage.resetAndGetPointerAddress());
if (isPOSTRequest)
{
auto content = getPOSTContent (*postData);
requestMessage->put_Content (content.get());
}
if (headers != nullptr && ! headers->isEmpty())
addHttpHeaders (requestMessage, *headers);
return requestMessage;
webViewEnvironment = nullptr;
}
//==============================================================================
void setControlBounds (Rectangle<int> newBounds) const
{
if (webViewControl != nullptr)
if (webViewController != nullptr)
{
#if JUCE_WIN_PER_MONITOR_DPI_AWARE
if (auto* peer = owner.getTopLevelComponent()->getPeer())
newBounds = (newBounds.toDouble() * peer->getPlatformScaleFactor()).toNearestInt();
#endif
WinRTWrapper::ComPtr<IWebViewControlSite> site;
if (SUCCEEDED (webViewControl->QueryInterface (site.resetAndGetPointerAddress())))
site->put_Bounds ({ static_cast<FLOAT> (newBounds.getX()), static_cast<FLOAT> (newBounds.getY()),
static_cast<FLOAT> (newBounds.getWidth()), static_cast<FLOAT> (newBounds.getHeight()) });
webViewController->put_Bounds({ newBounds.getX(), newBounds.getY(),
newBounds.getRight(), newBounds.getBottom() });
}
}
void setControlVisible (bool shouldBeVisible) const
{
if (webViewControl != 0)
{
WinRTWrapper::ComPtr<IWebViewControlSite> site;
if (SUCCEEDED (webViewControl->QueryInterface (site.resetAndGetPointerAddress())))
site->put_IsVisible (shouldBeVisible);
}
if (webViewController != nullptr)
webViewController->put_IsVisible (shouldBeVisible);
}
//==============================================================================
WebBrowserComponent& owner;
WinRTWrapper::ComPtr<IWebViewControlProcess> webViewProcess;
WinRTWrapper::ComPtr<IWebViewControl> webViewControl;
WinRTWrapper::ComPtr<ICoreWebView2Environment> webViewEnvironment;
WinRTWrapper::ComPtr<ICoreWebView2Controller> webViewController;
WinRTWrapper::ComPtr<ICoreWebView2> webView;
EventRegistrationToken navigationStartingToken { 0 },
newWindowRequestedToken { 0 },
navigationCompletedToken { 0 };
windowCloseRequestedToken { 0 },
navigationCompletedToken { 0 },
webResourceRequestedToken { 0 };
struct URLRequest
{
String url;
StringArray headers;
MemoryBlock postData;
};
URLRequest urlRequest;
bool isCreating = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebView2)
};
#endif
@ -806,17 +695,12 @@ class WebBrowserComponent::Pimpl
public:
Pimpl (WebBrowserComponent& owner)
{
#if JUCE_USE_WINRT_WEBVIEW
auto windowsVersionInfo = getWindowsVersionInfo();
if (windowsVersionInfo.dwMajorVersion >= 10 && windowsVersionInfo.dwBuildNumber >= 17763)
{
#if JUCE_USE_WIN_WEBVIEW2
try
{
internal.reset (new WinRTWebView (owner));
internal.reset (new WebView2 (owner));
}
catch (std::runtime_error&) {}
}
#endif
if (internal == nullptr)