Browse Source

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

tags/2021-05-28
ed 5 years ago
parent
commit
87fcf2f353
7 changed files with 302 additions and 372 deletions
  1. +6
    -5
      docs/CMake API.txt
  2. +72
    -36
      extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h
  3. +1
    -1
      modules/juce_events/juce_events.cpp
  4. +11
    -4
      modules/juce_gui_extra/juce_gui_extra.cpp
  5. +13
    -12
      modules/juce_gui_extra/juce_gui_extra.h
  6. +4
    -3
      modules/juce_gui_extra/misc/juce_WebBrowserComponent.h
  7. +195
    -311
      modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp

+ 6
- 5
docs/CMake API.txt 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.


+ 72
- 36
extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h 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)


+ 1
- 1
modules/juce_events/juce_events.cpp 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


+ 11
- 4
modules/juce_gui_extra/juce_gui_extra.cpp 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
//==============================================================================


+ 13
- 12
modules/juce_gui_extra/juce_gui_extra.h 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.
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".
/** 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.
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


+ 4
- 3
modules/juce_gui_extra/misc/juce_WebBrowserComponent.h 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}
*/


+ 195
- 311
modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp View File

@@ -310,114 +310,97 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EventHandler)
};
};
#if JUCE_USE_WINRT_WEBVIEW
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32WebView)
};
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,
public Component,
public ComponentMovementWatcher
class WebView2 : public InternalWebViewType,
public Component,
public ComponentMovementWatcher
{
public:
WinRTWebView (WebBrowserComponent& o)
: ComponentMovementWatcher (&o),
owner (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)
{
using OperationType = IAsyncOperation<OperationResultType>;
using DelegateType = IAsyncOperationCompletedHandler<OperationResultType>;
struct EventDelegate : public RuntimeClass<RuntimeClassFlags<RuntimeClassType::Delegate>,
DelegateType,
FtmBase>
{
EventDelegate() = default;
~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)
template<class ArgType>
static String getUriStringFromArgs (ArgType* args)
{
WinRTWrapper::ComPtr<IUriRuntimeClass> uri;
args.get_Uri (uri.resetAndGetPointerAddress());
if (uri != nullptr)
if (args != nullptr)
{
HSTRING uriString;
uri->get_AbsoluteUri (&uriString);
LPWSTR uri;
args->get_Uri (&uri);
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);
}
}
void removeEventHandlers()
{
if (webViewControl != nullptr)
{
if (navigationStartingToken.value != 0)
webViewControl->remove_NavigationStarting (navigationStartingToken);
}).Get(), &navigationCompletedToken);
if (newWindowRequestedToken.value != 0)
webViewControl->remove_NewWindowRequested (newWindowRequestedToken);
webView->AddWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
if (navigationCompletedToken.value != 0)
webViewControl->remove_NavigationCompleted (navigationCompletedToken);
}
}
bool createWebViewProcess()
{
auto webViewControlProcessFactory
= WinRTWrapper::getInstance()->getWRLFactory<IWebViewControlProcessFactory> (RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcess);
webView->add_WebResourceRequested (Callback<ICoreWebView2WebResourceRequestedEventHandler> (
[this] (ICoreWebView2*, ICoreWebView2WebResourceRequestedEventArgs* args) -> HRESULT
{
if (urlRequest.url.isEmpty())
return S_OK;
if (webViewControlProcessFactory == nullptr)
{
jassertfalse;
return false;
}
WinRTWrapper::ComPtr<ICoreWebView2WebResourceRequest> request;
args->get_Request (request.resetAndGetPointerAddress());
auto webViewProcessOptions
= WinRTWrapper::getInstance()->activateInstance<IWebViewControlProcessOptions> (RuntimeClass_Windows_Web_UI_Interop_WebViewControlProcessOptions,
__uuidof (IWebViewControlProcessOptions));
auto uriString = getUriStringFromArgs (request.get());
webViewProcessOptions->put_PrivateNetworkClientServerCapability (WebViewControlProcessCapabilityState_Enabled);
webViewControlProcessFactory->CreateWithOptions (webViewProcessOptions.get(), webViewProcess.resetAndGetPointerAddress());
if (uriString == urlRequest.url
|| (uriString.endsWith ("/") && uriString.upToLastOccurrenceOf ("/", false, false) == urlRequest.url))
{
String method ("GET");
return webViewProcess != nullptr;
}
if (urlRequest.postData.getSize() > 0)
{
method = "POST";
void createWebViewControl()
{
if (auto* peer = getPeer())
{
ScopedValueSetter<bool> svs (isCreating, true);
WinRTWrapper::ComPtr<IStream> content (SHCreateMemStream ((BYTE*) urlRequest.postData.getData(),
(UINT) urlRequest.postData.getSize()));
request->put_Content (content.get());
}
WinRTWrapper::ComPtr<IAsyncOperation<WebViewControl*>> createWebViewAsyncOperation;
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());
}
}
webViewProcess->CreateWebViewControlAsync ((INT64) peer->getNativeHandle(), {},
createWebViewAsyncOperation.resetAndGetPointerAddress());
request->put_Method (method.toWideCharPointer());
waitForCompletion (createWebViewAsyncOperation.get(), webViewControl.resetAndGetPointerAddress());
urlRequest = {};
}
addEventHandlers();
componentMovedOrResized (true, true);
return S_OK;
}).Get(), &webResourceRequestedToken);
}
}
//==============================================================================
WinRTWrapper::ComPtr<IUriRuntimeClass> createURI (const String& url)
void removeEventHandlers()
{
auto uriRuntimeFactory
= WinRTWrapper::getInstance()->getWRLFactory <IUriRuntimeClassFactory> (RuntimeClass_Windows_Foundation_Uri);
if (uriRuntimeFactory == nullptr)
if (webView != nullptr)
{
jassertfalse;
return {};
}
if (navigationStartingToken.value != 0)
webView->remove_NavigationStarting (navigationStartingToken);
WinRTWrapper::ScopedHString hstr (url);
WinRTWrapper::ComPtr<IUriRuntimeClass> uriRuntimeClass;
uriRuntimeFactory->CreateUri (hstr.get(), uriRuntimeClass.resetAndGetPointerAddress());
if (newWindowRequestedToken.value != 0)
webView->remove_NewWindowRequested (newWindowRequestedToken);
return uriRuntimeClass;
}
if (windowCloseRequestedToken.value != 0)
webView->remove_WindowCloseRequested (windowCloseRequestedToken);
WinRTWrapper::ComPtr<IHttpContent> getPOSTContent (const MemoryBlock& postData)
{
auto factory = WinRTWrapper::getInstance()->getWRLFactory<IHttpStringContentFactory> (RuntimeClass_Windows_Web_Http_HttpStringContent);
if (navigationCompletedToken.value != 0)
webView->remove_NavigationCompleted (navigationCompletedToken);
if (factory == nullptr)
{
jassertfalse;
return {};
if (webResourceRequestedToken.value != 0)
{
webView->RemoveWebResourceRequestedFilter (L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_DOCUMENT);
webView->remove_WebResourceRequested (webResourceRequestedToken);
}
}
}
WinRTWrapper::ScopedHString hStr (postData.toString());
bool createWebViewEnvironment()
{
auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
WinRTWrapper::ComPtr<IHttpContent> content;
factory->CreateFromString (hStr.get(), content.resetAndGetPointerAddress());
auto hr = CreateCoreWebView2EnvironmentWithOptions (nullptr, nullptr, options.Get(),
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[this] (HRESULT, ICoreWebView2Environment* env) -> HRESULT
{
webViewEnvironment = env;
return S_OK;
}).Get());
return content;
return SUCCEEDED (hr);
}
WinRTWrapper::ComPtr<IHttpMethod> getMethod (bool isPOST)
void createWebView()
{
auto methodFactory = WinRTWrapper::getInstance()->getWRLFactory<IHttpMethodStatics> (RuntimeClass_Windows_Web_Http_HttpMethod);
isCreating = true;
if (methodFactory == nullptr)
if (auto* peer = getPeer())
{
jassertfalse;
return {};
}
WinRTWrapper::ComPtr<IHttpMethod> method;
if (isPOST)
methodFactory->get_Post (method.resetAndGetPointerAddress());
else
methodFactory->get_Get (method.resetAndGetPointerAddress());
webViewEnvironment->CreateCoreWebView2Controller ((HWND) peer->getNativeHandle(),
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler> (
[this] (HRESULT, ICoreWebView2Controller* controller) -> HRESULT
{
webViewController = controller;
controller->get_CoreWebView2 (webView.resetAndGetPointerAddress());
return method;
}
isCreating = false;
void addHttpHeaders (WinRTWrapper::ComPtr<IHttpRequestMessage>& requestMessage, const StringArray& headers)
{
WinRTWrapper::ComPtr<IHttpRequestHeaderCollection> headerCollection;
requestMessage->get_Headers (headerCollection.resetAndGetPointerAddress());
addEventHandlers();
componentMovedOrResized (true, true);
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());
if (webView != nullptr && urlRequest.url.isNotEmpty())
webView->Navigate (urlRequest.url.toWideCharPointer());
headerCollection->Append (headerName.get(), headerValue.get());
return S_OK;
}).Get());
}
}
WinRTWrapper::ComPtr<IHttpRequestMessage> createHttpRequestMessage (const String& url,
const StringArray* headers,
const MemoryBlock* postData)
void closeWebView()
{
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)
if (webViewController.get() != nullptr)
{
auto content = getPOSTContent (*postData);
requestMessage->put_Content (content.get());
webViewController->Close();
webViewController = nullptr;
webView = nullptr;
}
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 },
windowCloseRequestedToken { 0 },
navigationCompletedToken { 0 },
webResourceRequestedToken { 0 };
EventRegistrationToken navigationStartingToken { 0 },
newWindowRequestedToken { 0 },
navigationCompletedToken { 0 };
struct URLRequest
{
String url;
StringArray headers;
MemoryBlock postData;
};
URLRequest urlRequest;
bool isCreating = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebView2)
};
#endif
@@ -806,18 +695,13 @@ class WebBrowserComponent::Pimpl
public:
Pimpl (WebBrowserComponent& owner)
{
#if JUCE_USE_WINRT_WEBVIEW
auto windowsVersionInfo = getWindowsVersionInfo();
if (windowsVersionInfo.dwMajorVersion >= 10 && windowsVersionInfo.dwBuildNumber >= 17763)
{
try
{
internal.reset (new WinRTWebView (owner));
}
catch (std::runtime_error&) {}
}
#endif
#if JUCE_USE_WIN_WEBVIEW2
try
{
internal.reset (new WebView2 (owner));
}
catch (std::runtime_error&) {}
#endif
if (internal == nullptr)
internal.reset (new Win32WebView (owner));


Loading…
Cancel
Save