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:
parent
5467c57e23
commit
87fcf2f353
7 changed files with 308 additions and 378 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue