diff --git a/SampleApps/WebView2APISample/App.cpp b/SampleApps/WebView2APISample/App.cpp index 3ac52dd5..3bd1d819 100644 --- a/SampleApps/WebView2APISample/App.cpp +++ b/SampleApps/WebView2APISample/App.cpp @@ -1,214 +1,214 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "stdafx.h" - -#include "App.h" - -#include -#include -#include -#include -#include -#include - -#include "AppWindow.h" -#include "DpiUtil.h" - -HINSTANCE g_hInstance; -int g_nCmdShow; -bool g_autoTabHandle = true; -static std::map s_threads; - -static int RunMessagePump(); -static DWORD WINAPI ThreadProc(void* pvParam); -static void WaitForOtherThreads(); - -#define NEXT_PARAM_CONTAINS(command) \ - _wcsnicmp(nextParam.c_str(), command, ARRAYSIZE(command) - 1) == 0 - -int APIENTRY wWinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - PWSTR lpCmdLine, - int nCmdShow) -{ - g_hInstance = hInstance; - UNREFERENCED_PARAMETER(hPrevInstance); - g_nCmdShow = nCmdShow; - - // Default DPI awareness to PerMonitorV2. The commandline parameters can - // override this. - DPI_AWARENESS_CONTEXT dpiAwarenessContext = - DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; - std::wstring appId(L"EBWebView.SampleApp"); - std::wstring initialUri(L"https://www.bing.com"); - DWORD creationModeId = IDM_CREATION_MODE_WINDOWED; - - if (lpCmdLine && lpCmdLine[0]) - { - int paramCount = 0; - LPWSTR* params = CommandLineToArgvW(lpCmdLine, ¶mCount); - for (int i = 0; i < paramCount; ++i) - { - std::wstring nextParam; - if (params[i][0] == L'-') - { - if (params[i][1] == L'-') - { - nextParam.assign(params[i] + 2); - } - else - { - nextParam.assign(params[i] + 1); - } - } - if (NEXT_PARAM_CONTAINS(L"dpiunaware")) - { - dpiAwarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE; - } - else if (NEXT_PARAM_CONTAINS(L"dpisystemaware")) - { - dpiAwarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; - } - else if (NEXT_PARAM_CONTAINS(L"dpipermonitorawarev2")) - { - dpiAwarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; - } - else if (NEXT_PARAM_CONTAINS(L"dpipermonitoraware")) - { - dpiAwarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; - } - else if (NEXT_PARAM_CONTAINS(L"noinitialnavigation")) - { - initialUri = L""; - } - else if (NEXT_PARAM_CONTAINS(L"appid=")) - { - appId = nextParam.substr(nextParam.find(L'=') + 1); - } - else if (NEXT_PARAM_CONTAINS(L"initialUri=")) - { - initialUri = nextParam.substr(nextParam.find(L'=') + 1); - } - else if (NEXT_PARAM_CONTAINS(L"creationmode=")) - { - nextParam = nextParam.substr(nextParam.find(L'=') + 1); - if (NEXT_PARAM_CONTAINS(L"windowed")) - { - creationModeId = IDM_CREATION_MODE_WINDOWED; - } - else if (NEXT_PARAM_CONTAINS(L"visualdcomp")) - { - creationModeId = IDM_CREATION_MODE_VISUAL_DCOMP; - } - else if (NEXT_PARAM_CONTAINS(L"targetdcomp")) - { - creationModeId = IDM_CREATION_MODE_TARGET_DCOMP; - } -#ifdef USE_WEBVIEW2_WIN10 - else if (NEXT_PARAM_CONTAINS(L"visualwincomp")) - { - creationModeId = IDM_CREATION_MODE_VISUAL_WINCOMP; - } -#endif - } - } - LocalFree(params); - } - SetCurrentProcessExplicitAppUserModelID(appId.c_str()); - - DpiUtil::SetProcessDpiAwarenessContext(dpiAwarenessContext); - - new AppWindow(creationModeId, initialUri, true); - - int retVal = RunMessagePump(); - - WaitForOtherThreads(); - - return retVal; -} - -// Run the message pump for one thread. -static int RunMessagePump() -{ - HACCEL hAccelTable = - LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDC_WEBVIEW2APISAMPLE)); - - MSG msg; - - // Main message loop: - //! [MoveFocus0] - while (GetMessage(&msg, nullptr, 0, 0)) - { - if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) - { - // Calling IsDialogMessage handles Tab traversal automatically. If the - // app wants the platform to auto handle tab, then call IsDialogMessage - // before calling TranslateMessage/DispatchMessage. If the app wants to - // handle tabbing itself, then skip calling IsDialogMessage and call - // TranslateMessage/DispatchMessage directly. - if (!g_autoTabHandle || !IsDialogMessage(GetAncestor(msg.hwnd, GA_ROOT), &msg)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - } - //! [MoveFocus0] - - DWORD threadId = GetCurrentThreadId(); - auto it = s_threads.find(threadId); - if (it != s_threads.end()) - { - CloseHandle(it->second); - s_threads.erase(threadId); - } - - return (int)msg.wParam; -} - -// Make a new thread. -void CreateNewThread(UINT creationModeId) -{ - DWORD threadId; - HANDLE thread = CreateThread( - nullptr, 0, ThreadProc, reinterpret_cast(creationModeId), - STACK_SIZE_PARAM_IS_A_RESERVATION, &threadId); - s_threads.insert(std::pair(threadId, thread)); -} - -// This function is the starting point for new threads. It will open a new app window. -static DWORD WINAPI ThreadProc(void* pvParam) -{ - new AppWindow(reinterpret_cast(pvParam)); - return RunMessagePump(); -} - -// Called on the main thread. Wait for all other threads to complete before exiting. -static void WaitForOtherThreads() -{ - while (!s_threads.empty()) - { - std::vector threadHandles; - for (auto it = s_threads.begin(); it != s_threads.end(); ++it) - { - threadHandles.push_back(it->second); - } - - HANDLE* handleArray = threadHandles.data(); - DWORD dwIndex = MsgWaitForMultipleObjects( - static_cast(threadHandles.size()), threadHandles.data(), FALSE, - INFINITE, QS_ALLEVENTS); - - if (dwIndex == WAIT_OBJECT_0 + threadHandles.size()) - { - MSG msg; - while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } - } -} +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "App.h" + +#include +#include +#include +#include +#include +#include + +#include "AppWindow.h" +#include "DpiUtil.h" + +HINSTANCE g_hInstance; +int g_nCmdShow; +bool g_autoTabHandle = true; +static std::map s_threads; + +static int RunMessagePump(); +static DWORD WINAPI ThreadProc(void* pvParam); +static void WaitForOtherThreads(); + +#define NEXT_PARAM_CONTAINS(command) \ + _wcsnicmp(nextParam.c_str(), command, ARRAYSIZE(command) - 1) == 0 + +int APIENTRY wWinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + PWSTR lpCmdLine, + int nCmdShow) +{ + g_hInstance = hInstance; + UNREFERENCED_PARAMETER(hPrevInstance); + g_nCmdShow = nCmdShow; + + // Default DPI awareness to PerMonitorV2. The commandline parameters can + // override this. + DPI_AWARENESS_CONTEXT dpiAwarenessContext = + DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + std::wstring appId(L"EBWebView.SampleApp"); + std::wstring initialUri; + DWORD creationModeId = IDM_CREATION_MODE_WINDOWED; + + if (lpCmdLine && lpCmdLine[0]) + { + int paramCount = 0; + LPWSTR* params = CommandLineToArgvW(lpCmdLine, ¶mCount); + for (int i = 0; i < paramCount; ++i) + { + std::wstring nextParam; + if (params[i][0] == L'-') + { + if (params[i][1] == L'-') + { + nextParam.assign(params[i] + 2); + } + else + { + nextParam.assign(params[i] + 1); + } + } + if (NEXT_PARAM_CONTAINS(L"dpiunaware")) + { + dpiAwarenessContext = DPI_AWARENESS_CONTEXT_UNAWARE; + } + else if (NEXT_PARAM_CONTAINS(L"dpisystemaware")) + { + dpiAwarenessContext = DPI_AWARENESS_CONTEXT_SYSTEM_AWARE; + } + else if (NEXT_PARAM_CONTAINS(L"dpipermonitorawarev2")) + { + dpiAwarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2; + } + else if (NEXT_PARAM_CONTAINS(L"dpipermonitoraware")) + { + dpiAwarenessContext = DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE; + } + else if (NEXT_PARAM_CONTAINS(L"noinitialnavigation")) + { + initialUri = L"none"; + } + else if (NEXT_PARAM_CONTAINS(L"appid=")) + { + appId = nextParam.substr(nextParam.find(L'=') + 1); + } + else if (NEXT_PARAM_CONTAINS(L"initialUri=")) + { + initialUri = nextParam.substr(nextParam.find(L'=') + 1); + } + else if (NEXT_PARAM_CONTAINS(L"creationmode=")) + { + nextParam = nextParam.substr(nextParam.find(L'=') + 1); + if (NEXT_PARAM_CONTAINS(L"windowed")) + { + creationModeId = IDM_CREATION_MODE_WINDOWED; + } + else if (NEXT_PARAM_CONTAINS(L"visualdcomp")) + { + creationModeId = IDM_CREATION_MODE_VISUAL_DCOMP; + } + else if (NEXT_PARAM_CONTAINS(L"targetdcomp")) + { + creationModeId = IDM_CREATION_MODE_TARGET_DCOMP; + } +#ifdef USE_WEBVIEW2_WIN10 + else if (NEXT_PARAM_CONTAINS(L"visualwincomp")) + { + creationModeId = IDM_CREATION_MODE_VISUAL_WINCOMP; + } +#endif + } + } + LocalFree(params); + } + SetCurrentProcessExplicitAppUserModelID(appId.c_str()); + + DpiUtil::SetProcessDpiAwarenessContext(dpiAwarenessContext); + + new AppWindow(creationModeId, initialUri, true); + + int retVal = RunMessagePump(); + + WaitForOtherThreads(); + + return retVal; +} + +// Run the message pump for one thread. +static int RunMessagePump() +{ + HACCEL hAccelTable = + LoadAccelerators(g_hInstance, MAKEINTRESOURCE(IDC_WEBVIEW2APISAMPLE)); + + MSG msg; + + // Main message loop: + //! [MoveFocus0] + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + // Calling IsDialogMessage handles Tab traversal automatically. If the + // app wants the platform to auto handle tab, then call IsDialogMessage + // before calling TranslateMessage/DispatchMessage. If the app wants to + // handle tabbing itself, then skip calling IsDialogMessage and call + // TranslateMessage/DispatchMessage directly. + if (!g_autoTabHandle || !IsDialogMessage(GetAncestor(msg.hwnd, GA_ROOT), &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + //! [MoveFocus0] + + DWORD threadId = GetCurrentThreadId(); + auto it = s_threads.find(threadId); + if (it != s_threads.end()) + { + CloseHandle(it->second); + s_threads.erase(threadId); + } + + return (int)msg.wParam; +} + +// Make a new thread. +void CreateNewThread(UINT creationModeId) +{ + DWORD threadId; + HANDLE thread = CreateThread( + nullptr, 0, ThreadProc, reinterpret_cast(creationModeId), + STACK_SIZE_PARAM_IS_A_RESERVATION, &threadId); + s_threads.insert(std::pair(threadId, thread)); +} + +// This function is the starting point for new threads. It will open a new app window. +static DWORD WINAPI ThreadProc(void* pvParam) +{ + new AppWindow(reinterpret_cast(pvParam)); + return RunMessagePump(); +} + +// Called on the main thread. Wait for all other threads to complete before exiting. +static void WaitForOtherThreads() +{ + while (!s_threads.empty()) + { + std::vector threadHandles; + for (auto it = s_threads.begin(); it != s_threads.end(); ++it) + { + threadHandles.push_back(it->second); + } + + HANDLE* handleArray = threadHandles.data(); + DWORD dwIndex = MsgWaitForMultipleObjects( + static_cast(threadHandles.size()), threadHandles.data(), FALSE, + INFINITE, QS_ALLEVENTS); + + if (dwIndex == WAIT_OBJECT_0 + threadHandles.size()) + { + MSG msg; + while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +} diff --git a/SampleApps/WebView2APISample/AppStartPage.cpp b/SampleApps/WebView2APISample/AppStartPage.cpp new file mode 100644 index 00000000..594438d7 --- /dev/null +++ b/SampleApps/WebView2APISample/AppStartPage.cpp @@ -0,0 +1,109 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include +#include +#include + +#include "AppStartPage.h" +#include "AppWindow.h" +#include "CheckFailure.h" + +using namespace Microsoft::WRL; + +namespace AppStartPage +{ + +bool AreFileUrisEqual(std::wstring leftUri, std::wstring rightUri) +{ + // Have to to lower due to current bug + std::transform(leftUri.begin(), leftUri.end(), + leftUri.begin(), ::tolower); + std::transform(rightUri.begin(), rightUri.end(), + rightUri.begin(), ::tolower); + + return leftUri == rightUri; +} + +std::wstring ResolvePathAndTrimFile(std::wstring path) +{ + wchar_t resultPath[MAX_PATH]; + PathCchCanonicalize(resultPath, ARRAYSIZE(resultPath), path.c_str()); + PathCchRemoveFileSpec(resultPath, ARRAYSIZE(resultPath)); + return resultPath; +} + +std::wstring GetSdkBuild() +{ + auto options = Microsoft::WRL::Make(); + wil::unique_cotaskmem_string targetVersion; + CHECK_FAILURE(options->get_TargetCompatibleBrowserVersion(&targetVersion)); + + // The full version string A.B.C.D + const wchar_t* targetVersionMajorAndRest = targetVersion.get(); + // Should now be .B.C.D + const wchar_t* targetVersionMinorAndRest = wcschr(targetVersionMajorAndRest, L'.'); + CHECK_FAILURE((targetVersionMinorAndRest != nullptr && *targetVersionMinorAndRest == L'.') ? S_OK : E_UNEXPECTED); + + // Should now be .C.D + const wchar_t* targetVersionBuildAndRest = wcschr(targetVersionMinorAndRest + 1, L'.'); + CHECK_FAILURE((targetVersionBuildAndRest != nullptr && *targetVersionBuildAndRest == L'.') ? S_OK : E_UNEXPECTED); + + // Return + 1 to skip the first . so just C.D + return targetVersionBuildAndRest + 1; +} + +std::wstring GetRuntimeVersion(AppWindow* appWindow) +{ + wil::com_ptr environment = appWindow->GetWebViewEnvironment(); + wil::unique_cotaskmem_string runtimeVersion; + CHECK_FAILURE(environment->get_BrowserVersionString(&runtimeVersion)); + + return runtimeVersion.get(); +} + +std::wstring GetAppPath() +{ + wchar_t appPath[MAX_PATH]; + GetModuleFileName(nullptr, appPath, ARRAYSIZE(appPath)); + return ResolvePathAndTrimFile(appPath); +} + +std::wstring GetRuntimePath(AppWindow* appWindow) +{ + wil::com_ptr webview = appWindow->GetWebView(); + UINT32 browserProcessId = 0; + wchar_t runtimePath[MAX_PATH]; + CHECK_FAILURE(webview->get_BrowserProcessId(&browserProcessId)); + + HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, browserProcessId); + CHECK_FAILURE(processHandle == nullptr ? E_FAIL : S_OK); + GetModuleFileNameEx(processHandle, nullptr, runtimePath, ARRAYSIZE(runtimePath)); + CloseHandle(processHandle); + + return ResolvePathAndTrimFile(runtimePath); +} + +std::wstring GetUri(AppWindow* appWindow) +{ + std::wstring uri = appWindow->GetLocalUri(L"AppStartPage.html"); + + uri += L"?sdkBuild="; + uri += GetSdkBuild(); + + uri += L"&runtimeVersion="; + uri += GetRuntimeVersion(appWindow); + + uri += L"&appPath="; + uri += GetAppPath(); + + uri += L"&runtimePath="; + uri += GetRuntimePath(appWindow); + + return uri; +} + +}; // namespace AppStartPage \ No newline at end of file diff --git a/SampleApps/WebView2APISample/AppStartPage.h b/SampleApps/WebView2APISample/AppStartPage.h new file mode 100644 index 00000000..98654a76 --- /dev/null +++ b/SampleApps/WebView2APISample/AppStartPage.h @@ -0,0 +1,15 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "stdafx.h" +#include + +class AppWindow; + +namespace AppStartPage +{ + std::wstring GetUri(AppWindow* appWindow); +}; diff --git a/SampleApps/WebView2APISample/AppStartPage.html b/SampleApps/WebView2APISample/AppStartPage.html new file mode 100644 index 00000000..1469a547 --- /dev/null +++ b/SampleApps/WebView2APISample/AppStartPage.html @@ -0,0 +1,153 @@ + + + + + Microsoft Edge WebView2 + + + +
+

Microsoft Edge WebView2

+
+ +
+
+
+
SDK build
...
+
Runtime version
...
+
App path
...
+
Runtime path
...
+
+
+
+ +
+
+

Issues

+
+ New issue +
+
+ +
+
+

Documentation

+
+ WebView2 documentation +
+
+ +
+
+

SDK releases

+
    +
+
+
+ + + +
+
+

Release Notes

+ +
+
+ + + + \ No newline at end of file diff --git a/SampleApps/WebView2APISample/AppStartPage.js b/SampleApps/WebView2APISample/AppStartPage.js new file mode 100644 index 00000000..235b9dba --- /dev/null +++ b/SampleApps/WebView2APISample/AppStartPage.js @@ -0,0 +1,56 @@ +(async function () { + async function uriToObject(uri) { + const responseFromFetch = await fetch(uri); + const responseAsText = await responseFromFetch.text(); + const response = JSON.parse(responseAsText); + return response; + } + + function parseQuery(query) { + if (query.startsWith("?")) { + query = query.substring(1); + } + + return query. + split("&"). + map(encodedNameValueStr => encodedNameValueStr.split("=")). + reduce((resultObject, encodedNameValueArr) => { + const nameValueArr = encodedNameValueArr.map(decodeURIComponent); + resultObject[nameValueArr[0]] = nameValueArr[1]; + return resultObject; + }, {}); + } + + const sdkReleasesNode = document.getElementById("sdkReleases"); + if (sdkReleasesNode) { + const nugetInfoUri = "https://azuresearch-usnc.nuget.org/query?q=PackageID%3aMicrosoft.Web.WebView2&prerelease=true&semVerLevel=2.0.0"; + const nugetInfo = await uriToObject(nugetInfoUri); + + let versions = nugetInfo.data[0].versions; + versions.reverse(); + versions.forEach(version => { + const versionText = version.version; + const aNode = document.createElement("a"); + aNode.href = "https://www.nuget.org/packages/Microsoft.Web.WebView2/" + versionText; + aNode.textContent = "WebView2 SDK " + versionText; + + const itemNode = document.createElement("li"); + itemNode.appendChild(aNode); + + sdkReleasesNode.appendChild(itemNode); + }); + } + + const query = parseQuery(location.search); + const fillIds = ["sdkBuild", "runtimeVersion", "appPath", "runtimePath"]; + fillIds.forEach(id => { + let content = query[id]; + if (content) { + const maxContentLength = 100; + if (content.length > maxContentLength) { + content = "..." + content.substring(content.length - maxContentLength); + } + document.getElementById(id).textContent = content; + } + }) +})(); \ No newline at end of file diff --git a/SampleApps/WebView2APISample/AppStartPageBackground.png b/SampleApps/WebView2APISample/AppStartPageBackground.png new file mode 100644 index 00000000..271b6481 Binary files /dev/null and b/SampleApps/WebView2APISample/AppStartPageBackground.png differ diff --git a/SampleApps/WebView2APISample/AppWindow.cpp b/SampleApps/WebView2APISample/AppWindow.cpp index 17f05097..6e2a7b22 100644 --- a/SampleApps/WebView2APISample/AppWindow.cpp +++ b/SampleApps/WebView2APISample/AppWindow.cpp @@ -1,1238 +1,1279 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "stdafx.h" - -#include "AppWindow.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include "App.h" -#include "CheckFailure.h" -#include "ControlComponent.h" -#include "DpiUtil.h" -#include "FileComponent.h" -#include "ProcessComponent.h" -#include "Resource.h" -#include "ScenarioAddHostObject.h" -#include "ScenarioAuthentication.h" -#include "ScenarioWebMessage.h" -#include "ScenarioWebViewEventMonitor.h" -#include "ScriptComponent.h" -#include "SettingsComponent.h" -#include "TextInputDialog.h" -#include "ViewComponent.h" -using namespace Microsoft::WRL; -static constexpr size_t s_maxLoadString = 100; -static constexpr UINT s_runAsyncWindowMessage = WM_APP; - -static thread_local size_t s_appInstances = 0; -// The minimum height and width for Window Features. -// See https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Size -static constexpr int s_minNewWindowSize = 100; - -// Run Download and Install in another thread so we don't block the UI thread -DWORD WINAPI DownloadAndInstallWV2RT(_In_ LPVOID lpParameter) -{ - AppWindow* appWindow = (AppWindow*) lpParameter; - - int returnCode = 2; // Download failed - // Use fwlink to download WebView2 Bootstrapper at runtime and invoke installation - // Broken/Invalid Https Certificate will fail to download - // Use of the download link below is governed by the below terms. You may acquire the link for your use at https://developer.microsoft.com/microsoft-edge/webview2/. - // Microsoft owns all legal right, title, and interest in and to the WebView2 Runtime Bootstrapper ("Software") and related documentation, - // including any intellectual property in the Software. - // You must acquire all code, including any code obtained from a Microsoft URL, under a separate license directly from Microsoft, including a Microsoft download site - // (e.g., https://developer.microsoft.com/microsoft-edge/webview2/). - HRESULT hr = URLDownloadToFile(NULL, L"https://go.microsoft.com/fwlink/p/?LinkId=2124703", L".\\MicrosoftEdgeWebview2Setup.exe", 0, 0); - if (hr == S_OK) - { - // Either Package the WebView2 Bootstrapper with your app or download it using fwlink - // Then invoke install at Runtime. - SHELLEXECUTEINFO shExInfo = {0}; - shExInfo.cbSize = sizeof(shExInfo); - shExInfo.fMask = SEE_MASK_NOASYNC; - shExInfo.hwnd = 0; - shExInfo.lpVerb = L"runas"; - shExInfo.lpFile = L"MicrosoftEdgeWebview2Setup.exe"; - shExInfo.lpParameters = L" /silent /install"; - shExInfo.lpDirectory = 0; - shExInfo.nShow = 0; - shExInfo.hInstApp = 0; - - if (ShellExecuteEx(&shExInfo)) - { - returnCode = 0; // Install successfull - } - else - { - returnCode = 1; // Install failed - } - } - - appWindow->InstallComplete(returnCode); - appWindow->Release(); - return returnCode; -} - -// Creates a new window which is a copy of the entire app, but on the same thread. -AppWindow::AppWindow( - UINT creationModeId, - std::wstring initialUri, - bool isMainWindow, - std::function webviewCreatedCallback, - bool customWindowRect, - RECT windowRect, - bool shouldHaveToolbar) - : m_creationModeId(creationModeId), - m_initialUri(initialUri), - m_onWebViewFirstInitialized(webviewCreatedCallback) -{ - // Initialize COM as STA. - CHECK_FAILURE(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); - - ++s_appInstances; - - WCHAR szTitle[s_maxLoadString]; // The title bar text - LoadStringW(g_hInstance, IDS_APP_TITLE, szTitle, s_maxLoadString); - - if (customWindowRect) - { - m_mainWindow = CreateWindowExW( - WS_EX_CONTROLPARENT, GetWindowClass(), szTitle, WS_OVERLAPPEDWINDOW, windowRect.left, - windowRect.top, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, nullptr, nullptr, g_hInstance, nullptr); - } - else - { - m_mainWindow = CreateWindowExW( - WS_EX_CONTROLPARENT, GetWindowClass(), szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, - 0, CW_USEDEFAULT, 0, nullptr, nullptr, g_hInstance, nullptr); - } - - SetWindowLongPtr(m_mainWindow, GWLP_USERDATA, (LONG_PTR)this); - -#ifdef USE_WEBVIEW2_WIN10 - //! [TextScaleChanged1] - if (winrt::try_get_activation_factory()) - { - m_uiSettings = winrt::Windows::UI::ViewManagement::UISettings(); - m_uiSettings.TextScaleFactorChanged({ this, &AppWindow::OnTextScaleChanged }); - } - //! [TextScaleChanged1] -#endif - - if (shouldHaveToolbar) - { - m_toolbar.Initialize(this); - } - - UpdateCreationModeMenu(); - ShowWindow(m_mainWindow, g_nCmdShow); - UpdateWindow(m_mainWindow); - - // If no WebVieRuntime installed, create new thread to do install/download. - // Otherwise just initialize webview. - wil::unique_cotaskmem_string version_info; - HRESULT hr = GetAvailableCoreWebView2BrowserVersionString(nullptr, &version_info); - if (hr == S_OK && version_info != nullptr) - { - RunAsync([this] { - InitializeWebView(); - }); - } - else - { - if (isMainWindow) { - AddRef(); - CreateThread(0, 0, DownloadAndInstallWV2RT, (void*) this, 0, 0); - } - else - { - MessageBox(m_mainWindow, L"WebView Runtime not installed", L"WebView Runtime Installation status", MB_OK); - } - } -} - -// Register the Win32 window class for the app window. -PCWSTR AppWindow::GetWindowClass() -{ - // Only do this once - static PCWSTR windowClass = [] { - static WCHAR windowClass[s_maxLoadString]; - LoadStringW(g_hInstance, IDC_WEBVIEW2APISAMPLE, windowClass, s_maxLoadString); - - WNDCLASSEXW wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProcStatic; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = g_hInstance; - wcex.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_WEBVIEW2APISAMPLE)); - wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WEBVIEW2APISAMPLE); - wcex.lpszClassName = windowClass; - wcex.hIconSm = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SMALL)); - - RegisterClassExW(&wcex); - return windowClass; - }(); - return windowClass; -} - -LRESULT CALLBACK AppWindow::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - if (auto app = (AppWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA)) - { - LRESULT result = 0; - if (app->HandleWindowMessage(hWnd, message, wParam, lParam, &result)) - { - return result; - } - } - return DefWindowProc(hWnd, message, wParam, lParam); -} - -// Handle Win32 window messages sent to the main window -bool AppWindow::HandleWindowMessage( - HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) -{ - // Give all components a chance to handle the message first. - for (auto& component : m_components) - { - if (component->HandleWindowMessage(hWnd, message, wParam, lParam, result)) - { - return true; - } - } - - switch (message) - { - case WM_SIZE: - { - // Don't resize the app or webview when the app is minimized - // let WM_SYSCOMMAND to handle it - if (lParam != 0) - { - ResizeEverything(); - return true; - } - } - break; - //! [DPIChanged] - case WM_DPICHANGED: - { - m_toolbar.UpdateDpiAndTextScale(); - RECT* const newWindowSize = reinterpret_cast(lParam); - SetWindowPos(hWnd, - nullptr, - newWindowSize->left, - newWindowSize->top, - newWindowSize->right - newWindowSize->left, - newWindowSize->bottom - newWindowSize->top, - SWP_NOZORDER | SWP_NOACTIVATE); - return true; - } - break; - //! [DPIChanged] - case WM_PAINT: - { - PAINTSTRUCT ps; - BeginPaint(hWnd, &ps); - EndPaint(hWnd, &ps); - return true; - } - break; - case s_runAsyncWindowMessage: - { - auto* task = reinterpret_cast*>(wParam); - (*task)(); - delete task; - return true; - } - break; - case WM_NCDESTROY: - { - int retValue = 0; - SetWindowLongPtr(hWnd, GWLP_USERDATA, NULL); - NotifyClosed(); - if (--s_appInstances == 0) - { - PostQuitMessage(retValue); - } - } - break; - //! [RestartManager] - case WM_QUERYENDSESSION: - { - // yes, we can shut down - // Register how we might be restarted - RegisterApplicationRestart(L"--restore", RESTART_NO_CRASH | RESTART_NO_HANG); - *result = TRUE; - return true; - } - break; - case WM_ENDSESSION: - { - if (wParam == TRUE) - { - // save app state and exit. - PostQuitMessage(0); - return true; - } - } - break; - //! [RestartManager] - case WM_KEYDOWN: - { - // If bit 30 is set, it means the WM_KEYDOWN message is autorepeated. - // We want to ignore it in that case. - if (!(lParam & (1 << 30))) - { - if (auto action = GetAcceleratorKeyFunction((UINT)wParam)) - { - action(); - return true; - } - } - } - break; - case WM_COMMAND: - { - return ExecuteWebViewCommands(wParam, lParam) || ExecuteAppCommands(wParam, lParam); - } - break; - } - return false; -} - -// Handle commands related to the WebView. -// This will do nothing if the WebView is not initialized. -bool AppWindow::ExecuteWebViewCommands(WPARAM wParam, LPARAM lParam) -{ - if (!m_webView) - return false; - switch (LOWORD(wParam)) - { - case IDM_GET_BROWSER_VERSION_AFTER_CREATION: - { - //! [GetBrowserVersionString] - wil::unique_cotaskmem_string version_info; - m_webViewEnvironment->get_BrowserVersionString(&version_info); - MessageBox( - m_mainWindow, version_info.get(), L"Browser Version Info After WebView Creation", - MB_OK); - //! [GetBrowserVersionString] - return true; - } - case IDM_CLOSE_WEBVIEW: - { - CloseWebView(); - return true; - } - case IDM_CLOSE_WEBVIEW_CLEANUP: - { - CloseWebView(true); - return true; - } - case IDM_SCENARIO_POST_WEB_MESSAGE: - { - NewComponent(this); - return true; - } - case IDM_SCENARIO_ADD_HOST_OBJECT: - { - NewComponent(this); - return true; - } - case IDM_SCENARIO_WEB_VIEW_EVENT_MONITOR: - { - NewComponent(this); - return true; - } - case IDM_SCENARIO_JAVA_SCRIPT: - { - WCHAR c_scriptPath[] = L"ScenarioJavaScriptDebugIndex.html"; - std::wstring m_scriptUri = GetLocalUri(c_scriptPath); - CHECK_FAILURE(m_webView->Navigate(m_scriptUri.c_str())); - return true; - } - case IDM_SCENARIO_TYPE_SCRIPT: - { - WCHAR c_scriptPath[] = L"ScenarioTypeScriptDebugIndex.html"; - std::wstring m_scriptUri = GetLocalUri(c_scriptPath); - CHECK_FAILURE(m_webView->Navigate(m_scriptUri.c_str())); - } - } - return false; -} -// Handle commands not related to the WebView, which will work even if the WebView -// is not currently initialized. -bool AppWindow::ExecuteAppCommands(WPARAM wParam, LPARAM lParam) -{ - switch (LOWORD(wParam)) - { - case IDM_ABOUT: - DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), m_mainWindow, About); - return true; - case IDM_GET_BROWSER_VERSION_BEFORE_CREATION: - { - wil::unique_cotaskmem_string version_info; - GetAvailableCoreWebView2BrowserVersionString(nullptr, &version_info); - MessageBox( - m_mainWindow, version_info.get(), L"Browser Version Info Before WebView Creation", - MB_OK); - return true; - } - case IDM_EXIT: - CloseAppWindow(); - return true; - case IDM_CREATION_MODE_WINDOWED: - case IDM_CREATION_MODE_VISUAL_DCOMP: - case IDM_CREATION_MODE_TARGET_DCOMP: -#ifdef USE_WEBVIEW2_WIN10 - case IDM_CREATION_MODE_VISUAL_WINCOMP: -#endif - m_creationModeId = LOWORD(wParam); - UpdateCreationModeMenu(); - return true; - case IDM_REINIT: - InitializeWebView(); - return true; - case IDM_TOGGLE_FULLSCREEN_ALLOWED: - { - m_fullScreenAllowed = !m_fullScreenAllowed; - MessageBox( - nullptr, - (std::wstring(L"Fullscreen is now ") + - (m_fullScreenAllowed ? L"allowed" : L"disallowed")) - .c_str(), - L"", MB_OK); - return true; - } - case IDM_NEW_WINDOW: - new AppWindow(m_creationModeId); - return true; - case IDM_NEW_THREAD: - CreateNewThread(m_creationModeId); - return true; - case IDM_SET_LANGUAGE: - ChangeLanguage(); - return true; - case IDM_TOGGLE_AAD_SSO: - ToggleAADSSO(); - return true; - } - return false; -} - -// Prompt the user for a new language string -void AppWindow::ChangeLanguage() -{ - TextInputDialog dialog( - GetMainWindow(), L"Language", L"Language:", - L"Enter a language to use for WebView, or leave blank to restore default.", - m_language.empty() ? L"zh-cn" : m_language.c_str()); - if (dialog.confirmed) - { - m_language = (dialog.input); - } -} - -// Toggle AAD SSO enabled -void AppWindow::ToggleAADSSO() -{ - m_AADSSOEnabled = !m_AADSSOEnabled; - MessageBox( - nullptr, - m_AADSSOEnabled ? L"AAD single sign on will be enabled for new WebView " - L"created after all webviews are closed." : - L"AAD single sign on will be disabled for new WebView" - L" created after all webviews are closed.", - L"AAD SSO change", - MB_OK); -} - -// Message handler for about dialog. -INT_PTR CALLBACK AppWindow::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_INITDIALOG: - return (INT_PTR)TRUE; - - case WM_COMMAND: - if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) - { - EndDialog(hDlg, LOWORD(wParam)); - return (INT_PTR)TRUE; - } - break; - } - return (INT_PTR)FALSE; -} - -// Decide what to do when an accelerator key is pressed. Instead of immediately performing -// the action, we hand it to the caller so they can decide whether to run it right away -// or running it asynchronously. Will return nullptr if there is no action for the key. -std::function AppWindow::GetAcceleratorKeyFunction(UINT key) -{ - if (GetKeyState(VK_CONTROL) < 0) - { - switch (key) - { - case 'N': - return [this] { new AppWindow(m_creationModeId); }; - case 'Q': - return [this] { CloseAppWindow(); }; - case 'S': - return [this] { - if (auto file = GetComponent()) - { - file->SaveScreenshot(); - } - }; - case 'T': - return [this] { CreateNewThread(m_creationModeId); }; - case 'W': - return [this] { CloseWebView(); }; - } - } - return nullptr; -} - -//! [CreateCoreWebView2Controller] -// Create or recreate the WebView and its environment. -void AppWindow::InitializeWebView() -{ - // To ensure browser switches get applied correctly, we need to close - // the existing WebView. This will result in a new browser process - // getting created which will apply the browser switches. - CloseWebView(); - m_dcompDevice = nullptr; -#ifdef USE_WEBVIEW2_WIN10 - m_wincompCompositor = nullptr; -#endif - LPCWSTR subFolder = nullptr; - - if (m_creationModeId == IDM_CREATION_MODE_VISUAL_DCOMP || - m_creationModeId == IDM_CREATION_MODE_TARGET_DCOMP) - { - HRESULT hr = DCompositionCreateDevice2(nullptr, IID_PPV_ARGS(&m_dcompDevice)); - if (!SUCCEEDED(hr)) - { - MessageBox( - m_mainWindow, - L"Attempting to create WebView using DComp Visual is not supported.\r\n" - "DComp device creation failed.\r\n" - "Current OS may not support DComp.", - L"Create with Windowless DComp Visual Failed", MB_OK); - return; - } - } -#ifdef USE_WEBVIEW2_WIN10 - else if (m_creationModeId == IDM_CREATION_MODE_VISUAL_WINCOMP) - { - HRESULT hr = TryCreateDispatcherQueue(); - if (!SUCCEEDED(hr)) - { - MessageBox( - m_mainWindow, - L"Attempting to create WebView using WinComp Visual is not supported.\r\n" - "WinComp compositor creation failed.\r\n" - "Current OS may not support WinComp.", - L"Create with Windowless WinComp Visual Failed", MB_OK); - return; - } - m_wincompCompositor = winrtComp::Compositor(); - } -#endif - //! [CreateCoreWebView2EnvironmentWithOptions] - auto options = Microsoft::WRL::Make(); - CHECK_FAILURE(options->put_AllowSingleSignOnUsingOSPrimaryAccount( - m_AADSSOEnabled ? TRUE : FALSE)); - if (!m_language.empty()) - CHECK_FAILURE(options->put_Language(m_language.c_str())); - HRESULT hr = CreateCoreWebView2EnvironmentWithOptions( - subFolder, nullptr, options.Get(), - Callback( - this, &AppWindow::OnCreateEnvironmentCompleted) - .Get()); - //! [CreateCoreWebView2EnvironmentWithOptions] - if (!SUCCEEDED(hr)) - { - if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) - { - MessageBox( - m_mainWindow, - L"Couldn't find Edge installation. " - "Do you have a version installed that's compatible with this " - "WebView2 SDK version?", - nullptr, MB_OK); - } - else - { - ShowFailure(hr, L"Failed to create webview environment"); - } - } -} -// This is the callback passed to CreateWebViewEnvironmentWithOptions. -// Here we simply create the WebView. -HRESULT AppWindow::OnCreateEnvironmentCompleted( - HRESULT result, ICoreWebView2Environment* environment) -{ - CHECK_FAILURE(result); - m_webViewEnvironment = environment; - - auto webViewExperimentalEnvironment = - m_webViewEnvironment.try_query(); -#ifdef USE_WEBVIEW2_WIN10 - if (webViewExperimentalEnvironment && (m_dcompDevice || m_wincompCompositor)) -#else - if (webViewExperimentalEnvironment && m_dcompDevice) -#endif - { - CHECK_FAILURE(webViewExperimentalEnvironment->CreateCoreWebView2CompositionController( - m_mainWindow, - Callback< - ICoreWebView2ExperimentalCreateCoreWebView2CompositionControllerCompletedHandler>( - [this]( - HRESULT result, - ICoreWebView2ExperimentalCompositionController* compositionController) -> HRESULT { - auto controller = - wil::com_ptr(compositionController) - .query(); - return OnCreateCoreWebView2ControllerCompleted(result, controller.get()); - }) - .Get())); - } - else - { - CHECK_FAILURE(m_webViewEnvironment->CreateCoreWebView2Controller( - m_mainWindow, Callback( - this, &AppWindow::OnCreateCoreWebView2ControllerCompleted) - .Get())); - } - - return S_OK; -} -//! [CreateCoreWebView2Controller] - -// This is the callback passed to CreateCoreWebView2Controller. Here we initialize all WebView-related -// state and register most of our event handlers with the WebView. -HRESULT AppWindow::OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller) -{ - if (result == S_OK) - { - m_controller = controller; - wil::com_ptr coreWebView2; - CHECK_FAILURE(m_controller->get_CoreWebView2(&coreWebView2)); - // We should check for failure here because if this app is using a newer - // SDK version compared to the install of the Edge browser, the Edge - // browser might not have support for the latest version of the - // ICoreWebView2_N interface. - coreWebView2.query_to(&m_webView); - // Create components. These will be deleted when the WebView is closed. - NewComponent(this); - NewComponent(this); - NewComponent(this); - NewComponent( - this, m_webViewEnvironment.get(), m_oldSettingsComponent.get()); - m_oldSettingsComponent = nullptr; - NewComponent( - this, m_dcompDevice.get(), -#ifdef USE_WEBVIEW2_WIN10 - m_wincompCompositor, -#endif - m_creationModeId == IDM_CREATION_MODE_TARGET_DCOMP); - NewComponent(this, &m_toolbar); - - // We have a few of our own event handlers to register here as well - RegisterEventHandlers(); - - // Set the initial size of the WebView - ResizeEverything(); - - if (m_onWebViewFirstInitialized) - { - m_onWebViewFirstInitialized(); - m_onWebViewFirstInitialized = nullptr; - } - - if (!m_initialUri.empty()) - { - m_webView->Navigate(m_initialUri.c_str()); - } - } - else - { - ShowFailure(result, L"Failed to create webview"); - } - return S_OK; -} -void AppWindow::ReinitializeWebView() -{ - // Save the settings component from being deleted when the WebView is closed, so we can - // copy its properties to the next settings component. - m_oldSettingsComponent = MoveComponent(); - InitializeWebView(); -} - -void AppWindow::ReinitializeWebViewWithNewBrowser() -{ - // Save the settings component from being deleted when the WebView is closed, so we can - // copy its properties to the next settings component. - m_oldSettingsComponent = MoveComponent(); - - // Use the reference to the web view before we close it - UINT webviewProcessId = 0; - m_webView->get_BrowserProcessId(&webviewProcessId); - - // We need to close the current webviews and wait for the browser_process to exit - // This is so the new webviews don't use the old browser exe - CloseWebView(); - - // Make sure the browser process inside webview is closed - ProcessComponent::EnsureProcessIsClosed(webviewProcessId, 2000); - - InitializeWebView(); -} - -void AppWindow::RestartApp() -{ - // Use the reference to the web view before we close the app window - UINT webviewProcessId = 0; - m_webView->get_BrowserProcessId(&webviewProcessId); - - // To restart the app completely, first we close the current App Window - CloseAppWindow(); - - // Make sure the browser process inside webview is closed - ProcessComponent::EnsureProcessIsClosed(webviewProcessId, 2000); - - // Get the command line arguments used to start this app - // so we can re-create the process with them - LPWSTR args = GetCommandLineW(); - - STARTUPINFOW startup_info = {0}; - startup_info.cb = sizeof(startup_info); - PROCESS_INFORMATION temp_process_info = {}; - // Start a new process - if (!::CreateProcess( - nullptr, args, - nullptr, // default process attributes - nullptr, // default thread attributes - FALSE, // do not inherit handles - 0, - nullptr, // no environment - nullptr, // default current directory - &startup_info, &temp_process_info)) - { - // Log some error information if desired - } - - // Terminate this current process - ::exit(0); -} - -void AppWindow::RegisterEventHandlers() -{ - //! [ContainsFullScreenElementChanged] - // Register a handler for the ContainsFullScreenChanged event. - CHECK_FAILURE(m_webView->add_ContainsFullScreenElementChanged( - Callback( - [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT { - if (m_fullScreenAllowed) - { - CHECK_FAILURE( - sender->get_ContainsFullScreenElement(&m_containsFullscreenElement)); - if (m_containsFullscreenElement) - { - EnterFullScreen(); - } - else - { - ExitFullScreen(); - } - } - return S_OK; - }) - .Get(), - nullptr)); - //! [ContainsFullScreenElementChanged] - - //! [NewWindowRequested] - // Register a handler for the NewWindowRequested event. - // This handler will defer the event, create a new app window, and then once the - // new window is ready, it'll provide that new window's WebView as the response to - // the request. - CHECK_FAILURE(m_webView->add_NewWindowRequested( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2NewWindowRequestedEventArgs* args) { - wil::com_ptr deferral; - CHECK_FAILURE(args->GetDeferral(&deferral)); - AppWindow* newAppWindow; - - wil::com_ptr windowFeatures; - CHECK_FAILURE(args->get_WindowFeatures(&windowFeatures)); - - RECT windowRect = {0}; - UINT32 left = 0; - UINT32 top = 0; - UINT32 height = 0; - UINT32 width = 0; - BOOL shouldHaveToolbar = true; - - BOOL hasPosition = FALSE; - BOOL hasSize = FALSE; - CHECK_FAILURE(windowFeatures->HasPosition(&hasPosition)); - CHECK_FAILURE(windowFeatures->HasSize(&hasSize)); - - bool useDefaultWindow = true; - - if (!!hasPosition && !!hasSize) - { - CHECK_FAILURE(windowFeatures->get_Left(&left)); - CHECK_FAILURE(windowFeatures->get_Top(&top)); - CHECK_FAILURE(windowFeatures->get_Height(&height)); - CHECK_FAILURE(windowFeatures->get_Width(&width)); - useDefaultWindow = false; - } - CHECK_FAILURE(windowFeatures->get_Toolbar(&shouldHaveToolbar)); - - windowRect.left = left; - windowRect.right = left + (width < s_minNewWindowSize ? s_minNewWindowSize : width); - windowRect.top = top; - windowRect.bottom = top + (height < s_minNewWindowSize ? s_minNewWindowSize : height); - - if (!useDefaultWindow) - { - newAppWindow = new AppWindow(m_creationModeId, L"", false, nullptr, true, windowRect, !!shouldHaveToolbar); - } - else - { - newAppWindow = new AppWindow(m_creationModeId, L""); - } - newAppWindow->m_isPopupWindow = true; - newAppWindow->m_onWebViewFirstInitialized = [args, deferral, newAppWindow]() { - CHECK_FAILURE(args->put_NewWindow(newAppWindow->m_webView.get())); - CHECK_FAILURE(args->put_Handled(TRUE)); - CHECK_FAILURE(deferral->Complete()); - }; - - return S_OK; - }) - .Get(), - nullptr)); - //! [NewWindowRequested] - - //! [WindowCloseRequested] - // Register a handler for the WindowCloseRequested event. - // This handler will close the app window if it is not the main window. - CHECK_FAILURE(m_webView->add_WindowCloseRequested( - Callback([this]( - ICoreWebView2* sender, - IUnknown* args) { - if (m_isPopupWindow) - { - CloseAppWindow(); - } - return S_OK; - }).Get(), - nullptr)); - //! [WindowCloseRequested] - - //! [NewBrowserVersionAvailable] - // After the environment is successfully created, - // register a handler for the NewBrowserVersionAvailable event. - // This handler tells when there is a new Edge version available on the machine. - CHECK_FAILURE(m_webViewEnvironment->add_NewBrowserVersionAvailable( - Callback( - [this](ICoreWebView2Environment* sender, IUnknown* args) -> HRESULT { - std::wstring message = L"We detected there is a new version for the browser."; - if (m_webView) - { - message += L"Do you want to restart the app? \n\n"; - message += L"Click No if you only want to re-create the webviews. \n"; - message += L"Click Cancel for no action. \n"; - } - int response = MessageBox( - m_mainWindow, message.c_str(), L"New available version", - m_webView ? MB_YESNOCANCEL : MB_OK); - - if (response == IDYES) - { - RestartApp(); - } - else if (response == IDNO) - { - ReinitializeWebViewWithNewBrowser(); - } - else - { - // do nothing - } - - return S_OK; - }) - .Get(), - nullptr)); - //! [NewBrowserVersionAvailable] -} - -// Updates the sizing and positioning of everything in the window. -void AppWindow::ResizeEverything() -{ - RECT availableBounds = {0}; - GetClientRect(m_mainWindow, &availableBounds); - - if (!m_containsFullscreenElement) - { - availableBounds = m_toolbar.Resize(availableBounds); - } - - if (auto view = GetComponent()) - { - view->SetBounds(availableBounds); - } -} - -//! [Close] -// Close the WebView and deinitialize related state. This doesn't close the app window. -void AppWindow::CloseWebView(bool cleanupUserDataFolder) -{ - DeleteAllComponents(); - if (m_controller) - { - m_controller->Close(); - m_controller = nullptr; - m_webView = nullptr; - } - m_webViewEnvironment = nullptr; - if (cleanupUserDataFolder) - { - // For non-UWP apps, the default user data folder {Executable File Name}.WebView2 - // is in the same directory next to the app executable. If end - // developers specify userDataFolder during WebView environment - // creation, they would need to pass in that explicit value here. - // For more information about userDataFolder: - // https://docs.microsoft.com/microsoft-edge/webview2/reference/win32/webview2-idl#createcorewebview2environmentwithoptions - WCHAR userDataFolder[MAX_PATH] = L""; - // Obtain the absolute path for relative paths that include "./" or "../" - _wfullpath( - userDataFolder, GetLocalPath(L".WebView2", true).c_str(), MAX_PATH); - std::wstring userDataFolderPath(userDataFolder); - - std::wstring message = L"Are you sure you want to clean up the user data folder at\n"; - message += userDataFolderPath; - message += L"\n?\nWarning: This action is not reversible.\n\n"; - message += L"Click No if there are other open WebView instances.\n"; - - if (MessageBox(m_mainWindow, message.c_str(), L"Cleanup User Data Folder", MB_YESNO) == - IDYES) - { - CHECK_FAILURE(DeleteFileRecursive(userDataFolderPath)); - } - } -} -//! [Close] - -HRESULT AppWindow::DeleteFileRecursive(std::wstring path) -{ - wil::com_ptr fileOperation; - CHECK_FAILURE( - CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&fileOperation))); - - // Turn off all UI from being shown to the user during the operation. - CHECK_FAILURE(fileOperation->SetOperationFlags(FOF_NO_UI)); - - wil::com_ptr userDataFolder; - CHECK_FAILURE( - SHCreateItemFromParsingName(path.c_str(), NULL, IID_PPV_ARGS(&userDataFolder))); - - // Add the operation - CHECK_FAILURE(fileOperation->DeleteItem(userDataFolder.get(), NULL)); - CHECK_FAILURE(userDataFolder->Release()); - - // Perform the operation to delete the directory - CHECK_FAILURE(fileOperation->PerformOperations()); - - CHECK_FAILURE(fileOperation->Release()); - CoUninitialize(); - return S_OK; -} - -void AppWindow::CloseAppWindow() -{ - CloseWebView(); - DestroyWindow(m_mainWindow); -} - -void AppWindow::DeleteComponent(ComponentBase* component) -{ - for (auto iter = m_components.begin(); iter != m_components.end(); iter++) - { - if (iter->get() == component) - { - m_components.erase(iter); - return; - } - } -} - -void AppWindow::DeleteAllComponents() -{ - // Delete components in reverse order of initialization. - while (!m_components.empty()) - { - m_components.pop_back(); - } -} - -template std::unique_ptr AppWindow::MoveComponent() -{ - for (auto iter = m_components.begin(); iter != m_components.end(); iter++) - { - if (dynamic_cast(iter->get())) - { - auto wanted = reinterpret_cast&&>(std::move(*iter)); - m_components.erase(iter); - return std::move(wanted); - } - } - return nullptr; -} - -void AppWindow::SetTitleText(PCWSTR titleText) -{ - SetWindowText(m_mainWindow, titleText); -} - -RECT AppWindow::GetWindowBounds() -{ - RECT hwndBounds = {0}; - GetClientRect(m_mainWindow, &hwndBounds); - return hwndBounds; -} - -std::wstring AppWindow::GetLocalPath(std::wstring relativePath, bool keep_exe_path) -{ - WCHAR rawPath[MAX_PATH]; - GetModuleFileNameW(g_hInstance, rawPath, MAX_PATH); - std::wstring path(rawPath); - if (keep_exe_path) - { - path.append(relativePath); - } - else - { - std::size_t index = path.find_last_of(L"\\") + 1; - path.replace(index, path.length(), relativePath); - } - return path; -} -std::wstring AppWindow::GetLocalUri(std::wstring relativePath) -{ - std::wstring path = GetLocalPath(relativePath, false); - - wil::com_ptr uri; - CHECK_FAILURE(CreateUri(path.c_str(), Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME, 0, &uri)); - - wil::unique_bstr uriBstr; - CHECK_FAILURE(uri->GetAbsoluteUri(&uriBstr)); - return std::wstring(uriBstr.get()); -} - -void AppWindow::RunAsync(std::function callback) -{ - auto* task = new std::function(callback); - PostMessage(m_mainWindow, s_runAsyncWindowMessage, reinterpret_cast(task), 0); -} - -void AppWindow::EnterFullScreen() -{ - DWORD style = GetWindowLong(m_mainWindow, GWL_STYLE); - MONITORINFO monitor_info = {sizeof(monitor_info)}; - m_hMenu = ::GetMenu(m_mainWindow); - ::SetMenu(m_mainWindow, nullptr); - if (GetWindowRect(m_mainWindow, &m_previousWindowRect) && - GetMonitorInfo( - MonitorFromWindow(m_mainWindow, MONITOR_DEFAULTTOPRIMARY), &monitor_info)) - { - SetWindowLong(m_mainWindow, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); - SetWindowPos( - m_mainWindow, HWND_TOP, monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, - monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, - monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - } -} - -void AppWindow::ExitFullScreen() -{ - DWORD style = GetWindowLong(m_mainWindow, GWL_STYLE); - ::SetMenu(m_mainWindow, m_hMenu); - SetWindowLong(m_mainWindow, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); - SetWindowPos( - m_mainWindow, NULL, m_previousWindowRect.left, m_previousWindowRect.top, - m_previousWindowRect.right - m_previousWindowRect.left, - m_previousWindowRect.bottom - m_previousWindowRect.top, - SWP_NOOWNERZORDER | SWP_FRAMECHANGED); -} - -// We have our own implementation of DCompositionCreateDevice2 that dynamically -// loads dcomp.dll to create the device. Not having a static dependency on dcomp.dll -// enables the sample app to run on versions of Windows that don't support dcomp. -HRESULT AppWindow::DCompositionCreateDevice2(IUnknown* renderingDevice, REFIID riid, void** ppv) -{ - HRESULT hr = E_FAIL; - static decltype(::DCompositionCreateDevice2)* fnCreateDCompDevice2 = nullptr; - if (fnCreateDCompDevice2 == nullptr) - { - HMODULE hmod = ::LoadLibraryEx(L"dcomp.dll", nullptr, 0); - if (hmod != nullptr) - { - fnCreateDCompDevice2 = reinterpret_cast( - ::GetProcAddress(hmod, "DCompositionCreateDevice2")); - } - } - if (fnCreateDCompDevice2 != nullptr) - { - hr = fnCreateDCompDevice2(renderingDevice, riid, ppv); - } - return hr; -} - -// WinRT APIs cannot run without a DispatcherQueue. This helper function creates a -// DispatcherQueueController (which instantiates a DispatcherQueue under the covers) that will -// manage tasks for the WinRT APIs. The DispatcherQueue implementation lives in -// CoreMessaging.dll Similar to dcomp.dll, we load CoreMessaging.dll dynamically so the sample -// app can run on versions of windows that don't have CoreMessaging. -HRESULT AppWindow::TryCreateDispatcherQueue() -{ - namespace winSystem = winrt::Windows::System; - - HRESULT hr = S_OK; - thread_local winSystem::DispatcherQueueController dispatcherQueueController{ nullptr }; - - if (dispatcherQueueController == nullptr) - { - hr = E_FAIL; - static decltype(::CreateDispatcherQueueController)* fnCreateDispatcherQueueController = - nullptr; - if (fnCreateDispatcherQueueController == nullptr) - { - HMODULE hmod = ::LoadLibraryEx(L"CoreMessaging.dll", nullptr, 0); - if (hmod != nullptr) - { - fnCreateDispatcherQueueController = - reinterpret_cast( - ::GetProcAddress(hmod, "CreateDispatcherQueueController")); - } - } - if (fnCreateDispatcherQueueController != nullptr) - { - winSystem::DispatcherQueueController controller{ nullptr }; - DispatcherQueueOptions options - { - sizeof(DispatcherQueueOptions), - DQTYPE_THREAD_CURRENT, - DQTAT_COM_STA - }; - hr = fnCreateDispatcherQueueController( - options, reinterpret_cast( - winrt::put_abi(controller))); - dispatcherQueueController = controller; - } - } - - return hr; -} - -#ifdef USE_WEBVIEW2_WIN10 -//! [TextScaleChanged2] -void AppWindow::OnTextScaleChanged( - winrt::Windows::UI::ViewManagement::UISettings const& settings, - winrt::Windows::Foundation::IInspectable const& args) -{ - RunAsync([this] { - m_toolbar.UpdateDpiAndTextScale(); - }); -} -//! [TextScaleChanged2] -#endif -void AppWindow::UpdateCreationModeMenu() -{ - HMENU hMenu = GetMenu(m_mainWindow); - CheckMenuRadioItem( - hMenu, - IDM_CREATION_MODE_WINDOWED, -#ifdef USE_WEBVIEW2_WIN10 - IDM_CREATION_MODE_VISUAL_WINCOMP, -#else - IDM_CREATION_MODE_TARGET_DCOMP, -#endif - m_creationModeId, - MF_BYCOMMAND); -} - -double AppWindow::GetDpiScale() -{ - return DpiUtil::GetDpiForWindow(m_mainWindow) * 1.0f / USER_DEFAULT_SCREEN_DPI; -} - -#ifdef USE_WEBVIEW2_WIN10 -double AppWindow::GetTextScale() -{ - return m_uiSettings ? m_uiSettings.TextScaleFactor() : 1.0f; -} -#endif - -void AppWindow::AddRef() -{ - InterlockedIncrement((LONG *)&m_refCount); -} - -void AppWindow::Release() -{ - uint32_t refCount = InterlockedDecrement((LONG *)&m_refCount); - if (refCount == 0) - { - delete this; - } -} - -void AppWindow::NotifyClosed() -{ - m_isClosed = true; -} - -void AppWindow::InstallComplete(int return_code) -{ - if (!m_isClosed) - { - if (return_code == 0) - { - RunAsync([this] { - InitializeWebView(); - }); - } - else if (return_code == 1) - { - MessageBox(m_mainWindow, L"WebView Runtime failed to Install", L"WebView Runtime Installation status", MB_OK); - } - else if (return_code == 2) - { - MessageBox(m_mainWindow, L"WebView Bootstrapper failled to download", L"WebView Bootstrapper Download status", MB_OK); - } - } -} +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "AppWindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "App.h" +#include "AppStartPage.h" +#include "CheckFailure.h" +#include "ControlComponent.h" +#include "DpiUtil.h" +#include "FileComponent.h" +#include "ProcessComponent.h" +#include "Resource.h" +#include "ScenarioAddHostObject.h" +#include "ScenarioAuthentication.h" +#include "ScenarioCookieManagement.h" +#include "ScenarioDOMContentLoaded.h" +#include "ScenarioNavigateWithWebResourceRequest.h" +#include "ScenarioWebMessage.h" +#include "ScenarioWebViewEventMonitor.h" +#include "ScriptComponent.h" +#include "SettingsComponent.h" +#include "TextInputDialog.h" +#include "ViewComponent.h" +using namespace Microsoft::WRL; +static constexpr size_t s_maxLoadString = 100; +static constexpr UINT s_runAsyncWindowMessage = WM_APP; + +static thread_local size_t s_appInstances = 0; +// The minimum height and width for Window Features. +// See https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Size +static constexpr int s_minNewWindowSize = 100; + +// Run Download and Install in another thread so we don't block the UI thread +DWORD WINAPI DownloadAndInstallWV2RT(_In_ LPVOID lpParameter) +{ + AppWindow* appWindow = (AppWindow*) lpParameter; + + int returnCode = 2; // Download failed + // Use fwlink to download WebView2 Bootstrapper at runtime and invoke installation + // Broken/Invalid Https Certificate will fail to download + // Use of the download link below is governed by the below terms. You may acquire the link for your use at https://developer.microsoft.com/microsoft-edge/webview2/. + // Microsoft owns all legal right, title, and interest in and to the WebView2 Runtime Bootstrapper ("Software") and related documentation, + // including any intellectual property in the Software. + // You must acquire all code, including any code obtained from a Microsoft URL, under a separate license directly from Microsoft, including a Microsoft download site + // (e.g., https://developer.microsoft.com/microsoft-edge/webview2/). + HRESULT hr = URLDownloadToFile(NULL, L"https://go.microsoft.com/fwlink/p/?LinkId=2124703", L".\\MicrosoftEdgeWebview2Setup.exe", 0, 0); + if (hr == S_OK) + { + // Either Package the WebView2 Bootstrapper with your app or download it using fwlink + // Then invoke install at Runtime. + SHELLEXECUTEINFO shExInfo = {0}; + shExInfo.cbSize = sizeof(shExInfo); + shExInfo.fMask = SEE_MASK_NOASYNC; + shExInfo.hwnd = 0; + shExInfo.lpVerb = L"runas"; + shExInfo.lpFile = L"MicrosoftEdgeWebview2Setup.exe"; + shExInfo.lpParameters = L" /silent /install"; + shExInfo.lpDirectory = 0; + shExInfo.nShow = 0; + shExInfo.hInstApp = 0; + + if (ShellExecuteEx(&shExInfo)) + { + returnCode = 0; // Install successfull + } + else + { + returnCode = 1; // Install failed + } + } + + appWindow->InstallComplete(returnCode); + appWindow->Release(); + return returnCode; +} + +// Creates a new window which is a copy of the entire app, but on the same thread. +AppWindow::AppWindow( + UINT creationModeId, + std::wstring initialUri, + bool isMainWindow, + std::function webviewCreatedCallback, + bool customWindowRect, + RECT windowRect, + bool shouldHaveToolbar) + : m_creationModeId(creationModeId), + m_initialUri(initialUri), + m_onWebViewFirstInitialized(webviewCreatedCallback) +{ + // Initialize COM as STA. + CHECK_FAILURE(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)); + + ++s_appInstances; + + WCHAR szTitle[s_maxLoadString]; // The title bar text + LoadStringW(g_hInstance, IDS_APP_TITLE, szTitle, s_maxLoadString); + + if (customWindowRect) + { + m_mainWindow = CreateWindowExW( + WS_EX_CONTROLPARENT, GetWindowClass(), szTitle, WS_OVERLAPPEDWINDOW, windowRect.left, + windowRect.top, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, nullptr, nullptr, g_hInstance, nullptr); + } + else + { + m_mainWindow = CreateWindowExW( + WS_EX_CONTROLPARENT, GetWindowClass(), szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, + 0, CW_USEDEFAULT, 0, nullptr, nullptr, g_hInstance, nullptr); + } + + SetWindowLongPtr(m_mainWindow, GWLP_USERDATA, (LONG_PTR)this); + +#ifdef USE_WEBVIEW2_WIN10 + //! [TextScaleChanged1] + if (winrt::try_get_activation_factory()) + { + m_uiSettings = winrt::Windows::UI::ViewManagement::UISettings(); + m_uiSettings.TextScaleFactorChanged({ this, &AppWindow::OnTextScaleChanged }); + } + //! [TextScaleChanged1] +#endif + + if (shouldHaveToolbar) + { + m_toolbar.Initialize(this); + } + + UpdateCreationModeMenu(); + ShowWindow(m_mainWindow, g_nCmdShow); + UpdateWindow(m_mainWindow); + + // If no WebVieRuntime installed, create new thread to do install/download. + // Otherwise just initialize webview. + wil::unique_cotaskmem_string version_info; + HRESULT hr = GetAvailableCoreWebView2BrowserVersionString(nullptr, &version_info); + if (hr == S_OK && version_info != nullptr) + { + RunAsync([this] { + InitializeWebView(); + }); + } + else + { + if (isMainWindow) { + AddRef(); + CreateThread(0, 0, DownloadAndInstallWV2RT, (void*) this, 0, 0); + } + else + { + MessageBox(m_mainWindow, L"WebView Runtime not installed", L"WebView Runtime Installation status", MB_OK); + } + } +} + +// Register the Win32 window class for the app window. +PCWSTR AppWindow::GetWindowClass() +{ + // Only do this once + static PCWSTR windowClass = [] { + static WCHAR windowClass[s_maxLoadString]; + LoadStringW(g_hInstance, IDC_WEBVIEW2APISAMPLE, windowClass, s_maxLoadString); + + WNDCLASSEXW wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProcStatic; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = g_hInstance; + wcex.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_WEBVIEW2APISAMPLE)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_WEBVIEW2APISAMPLE); + wcex.lpszClassName = windowClass; + wcex.hIconSm = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + RegisterClassExW(&wcex); + return windowClass; + }(); + return windowClass; +} + +LRESULT CALLBACK AppWindow::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (auto app = (AppWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA)) + { + LRESULT result = 0; + if (app->HandleWindowMessage(hWnd, message, wParam, lParam, &result)) + { + return result; + } + } + return DefWindowProc(hWnd, message, wParam, lParam); +} + +// Handle Win32 window messages sent to the main window +bool AppWindow::HandleWindowMessage( + HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) +{ + // Give all components a chance to handle the message first. + for (auto& component : m_components) + { + if (component->HandleWindowMessage(hWnd, message, wParam, lParam, result)) + { + return true; + } + } + + switch (message) + { + case WM_SIZE: + { + // Don't resize the app or webview when the app is minimized + // let WM_SYSCOMMAND to handle it + if (lParam != 0) + { + ResizeEverything(); + return true; + } + } + break; + //! [DPIChanged] + case WM_DPICHANGED: + { + m_toolbar.UpdateDpiAndTextScale(); + RECT* const newWindowSize = reinterpret_cast(lParam); + SetWindowPos(hWnd, + nullptr, + newWindowSize->left, + newWindowSize->top, + newWindowSize->right - newWindowSize->left, + newWindowSize->bottom - newWindowSize->top, + SWP_NOZORDER | SWP_NOACTIVATE); + return true; + } + break; + //! [DPIChanged] + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + return true; + } + break; + case s_runAsyncWindowMessage: + { + auto* task = reinterpret_cast*>(wParam); + (*task)(); + delete task; + return true; + } + break; + case WM_NCDESTROY: + { + int retValue = 0; + SetWindowLongPtr(hWnd, GWLP_USERDATA, NULL); + NotifyClosed(); + if (--s_appInstances == 0) + { + PostQuitMessage(retValue); + } + } + break; + //! [RestartManager] + case WM_QUERYENDSESSION: + { + // yes, we can shut down + // Register how we might be restarted + RegisterApplicationRestart(L"--restore", RESTART_NO_CRASH | RESTART_NO_HANG); + *result = TRUE; + return true; + } + break; + case WM_ENDSESSION: + { + if (wParam == TRUE) + { + // save app state and exit. + PostQuitMessage(0); + return true; + } + } + break; + //! [RestartManager] + case WM_KEYDOWN: + { + // If bit 30 is set, it means the WM_KEYDOWN message is autorepeated. + // We want to ignore it in that case. + if (!(lParam & (1 << 30))) + { + if (auto action = GetAcceleratorKeyFunction((UINT)wParam)) + { + action(); + return true; + } + } + } + break; + case WM_COMMAND: + { + return ExecuteWebViewCommands(wParam, lParam) || ExecuteAppCommands(wParam, lParam); + } + break; + } + return false; +} + +// Handle commands related to the WebView. +// This will do nothing if the WebView is not initialized. +bool AppWindow::ExecuteWebViewCommands(WPARAM wParam, LPARAM lParam) +{ + if (!m_webView) + return false; + switch (LOWORD(wParam)) + { + case IDM_GET_BROWSER_VERSION_AFTER_CREATION: + { + //! [GetBrowserVersionString] + wil::unique_cotaskmem_string version_info; + m_webViewEnvironment->get_BrowserVersionString(&version_info); + MessageBox( + m_mainWindow, version_info.get(), L"Browser Version Info After WebView Creation", + MB_OK); + //! [GetBrowserVersionString] + return true; + } + case IDM_CLOSE_WEBVIEW: + { + CloseWebView(); + return true; + } + case IDM_CLOSE_WEBVIEW_CLEANUP: + { + CloseWebView(true); + return true; + } + case IDM_SCENARIO_POST_WEB_MESSAGE: + { + NewComponent(this); + return true; + } + case IDM_SCENARIO_ADD_HOST_OBJECT: + { + NewComponent(this); + return true; + } + case IDM_SCENARIO_WEB_VIEW_EVENT_MONITOR: + { + NewComponent(this); + return true; + } + case IDM_SCENARIO_JAVA_SCRIPT: + { + WCHAR c_scriptPath[] = L"ScenarioJavaScriptDebugIndex.html"; + std::wstring m_scriptUri = GetLocalUri(c_scriptPath); + CHECK_FAILURE(m_webView->Navigate(m_scriptUri.c_str())); + return true; + } + case IDM_SCENARIO_TYPE_SCRIPT: + { + WCHAR c_scriptPath[] = L"ScenarioTypeScriptDebugIndex.html"; + std::wstring m_scriptUri = GetLocalUri(c_scriptPath); + CHECK_FAILURE(m_webView->Navigate(m_scriptUri.c_str())); + } + case IDM_SCENARIO_AUTHENTICATION: + { + NewComponent(this); + + return true; + } + case IDM_SCENARIO_COOKIE_MANAGEMENT: + { + NewComponent(this); + return true; + } + case IDM_SCENARIO_DOM_CONTENT_LOADED: + { + NewComponent(this); + return true; + } + case IDM_SCENARIO_NAVIGATEWITHWEBRESOURCEREQUEST: + { + NewComponent(this); + return true; + } + } + return false; +} + +// Handle commands not related to the WebView, which will work even if the WebView +// is not currently initialized. +bool AppWindow::ExecuteAppCommands(WPARAM wParam, LPARAM lParam) +{ + switch (LOWORD(wParam)) + { + case IDM_ABOUT: + DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), m_mainWindow, About); + return true; + case IDM_GET_BROWSER_VERSION_BEFORE_CREATION: + { + wil::unique_cotaskmem_string version_info; + GetAvailableCoreWebView2BrowserVersionString(nullptr, &version_info); + MessageBox( + m_mainWindow, version_info.get(), L"Browser Version Info Before WebView Creation", + MB_OK); + return true; + } + case IDM_EXIT: + CloseAppWindow(); + return true; + case IDM_CREATION_MODE_WINDOWED: + case IDM_CREATION_MODE_VISUAL_DCOMP: + case IDM_CREATION_MODE_TARGET_DCOMP: +#ifdef USE_WEBVIEW2_WIN10 + case IDM_CREATION_MODE_VISUAL_WINCOMP: +#endif + m_creationModeId = LOWORD(wParam); + UpdateCreationModeMenu(); + return true; + case IDM_REINIT: + InitializeWebView(); + return true; + case IDM_TOGGLE_FULLSCREEN_ALLOWED: + { + m_fullScreenAllowed = !m_fullScreenAllowed; + MessageBox( + nullptr, + (std::wstring(L"Fullscreen is now ") + + (m_fullScreenAllowed ? L"allowed" : L"disallowed")) + .c_str(), + L"", MB_OK); + return true; + } + case IDM_NEW_WINDOW: + new AppWindow(m_creationModeId); + return true; + case IDM_NEW_THREAD: + CreateNewThread(m_creationModeId); + return true; + case IDM_SET_LANGUAGE: + ChangeLanguage(); + return true; + case IDM_TOGGLE_AAD_SSO: + ToggleAADSSO(); + return true; + } + return false; +} + +// Prompt the user for a new language string +void AppWindow::ChangeLanguage() +{ + TextInputDialog dialog( + GetMainWindow(), L"Language", L"Language:", + L"Enter a language to use for WebView, or leave blank to restore default.", + m_language.empty() ? L"zh-cn" : m_language.c_str()); + if (dialog.confirmed) + { + m_language = (dialog.input); + } +} + +// Toggle AAD SSO enabled +void AppWindow::ToggleAADSSO() +{ + m_AADSSOEnabled = !m_AADSSOEnabled; + MessageBox( + nullptr, + m_AADSSOEnabled ? L"AAD single sign on will be enabled for new WebView " + L"created after all webviews are closed." : + L"AAD single sign on will be disabled for new WebView" + L" created after all webviews are closed.", + L"AAD SSO change", + MB_OK); +} + +// Message handler for about dialog. +INT_PTR CALLBACK AppWindow::About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} + +// Decide what to do when an accelerator key is pressed. Instead of immediately performing +// the action, we hand it to the caller so they can decide whether to run it right away +// or running it asynchronously. Will return nullptr if there is no action for the key. +std::function AppWindow::GetAcceleratorKeyFunction(UINT key) +{ + if (GetKeyState(VK_CONTROL) < 0) + { + switch (key) + { + case 'N': + return [this] { new AppWindow(m_creationModeId); }; + case 'Q': + return [this] { CloseAppWindow(); }; + case 'S': + return [this] { + if (auto file = GetComponent()) + { + file->SaveScreenshot(); + } + }; + case 'T': + return [this] { CreateNewThread(m_creationModeId); }; + case 'W': + return [this] { CloseWebView(); }; + } + } + return nullptr; +} + +//! [CreateCoreWebView2Controller] +// Create or recreate the WebView and its environment. +void AppWindow::InitializeWebView() +{ + // To ensure browser switches get applied correctly, we need to close + // the existing WebView. This will result in a new browser process + // getting created which will apply the browser switches. + CloseWebView(); + m_dcompDevice = nullptr; +#ifdef USE_WEBVIEW2_WIN10 + m_wincompCompositor = nullptr; +#endif + LPCWSTR subFolder = nullptr; + + if (m_creationModeId == IDM_CREATION_MODE_VISUAL_DCOMP || + m_creationModeId == IDM_CREATION_MODE_TARGET_DCOMP) + { + HRESULT hr = DCompositionCreateDevice2(nullptr, IID_PPV_ARGS(&m_dcompDevice)); + if (!SUCCEEDED(hr)) + { + MessageBox( + m_mainWindow, + L"Attempting to create WebView using DComp Visual is not supported.\r\n" + "DComp device creation failed.\r\n" + "Current OS may not support DComp.", + L"Create with Windowless DComp Visual Failed", MB_OK); + return; + } + } +#ifdef USE_WEBVIEW2_WIN10 + else if (m_creationModeId == IDM_CREATION_MODE_VISUAL_WINCOMP) + { + HRESULT hr = TryCreateDispatcherQueue(); + if (!SUCCEEDED(hr)) + { + MessageBox( + m_mainWindow, + L"Attempting to create WebView using WinComp Visual is not supported.\r\n" + "WinComp compositor creation failed.\r\n" + "Current OS may not support WinComp.", + L"Create with Windowless WinComp Visual Failed", MB_OK); + return; + } + m_wincompCompositor = winrtComp::Compositor(); + } +#endif + //! [CreateCoreWebView2EnvironmentWithOptions] + auto options = Microsoft::WRL::Make(); + CHECK_FAILURE(options->put_AllowSingleSignOnUsingOSPrimaryAccount( + m_AADSSOEnabled ? TRUE : FALSE)); + if (!m_language.empty()) + CHECK_FAILURE(options->put_Language(m_language.c_str())); + HRESULT hr = CreateCoreWebView2EnvironmentWithOptions( + subFolder, nullptr, options.Get(), + Callback( + this, &AppWindow::OnCreateEnvironmentCompleted) + .Get()); + //! [CreateCoreWebView2EnvironmentWithOptions] + if (!SUCCEEDED(hr)) + { + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + { + MessageBox( + m_mainWindow, + L"Couldn't find Edge installation. " + "Do you have a version installed that's compatible with this " + "WebView2 SDK version?", + nullptr, MB_OK); + } + else + { + ShowFailure(hr, L"Failed to create webview environment"); + } + } +} +// This is the callback passed to CreateWebViewEnvironmentWithOptions. +// Here we simply create the WebView. +HRESULT AppWindow::OnCreateEnvironmentCompleted( + HRESULT result, ICoreWebView2Environment* environment) +{ + CHECK_FAILURE(result); + m_webViewEnvironment = environment; + + auto webViewExperimentalEnvironment = + m_webViewEnvironment.try_query(); +#ifdef USE_WEBVIEW2_WIN10 + if (webViewExperimentalEnvironment && (m_dcompDevice || m_wincompCompositor)) +#else + if (webViewExperimentalEnvironment && m_dcompDevice) +#endif + { + CHECK_FAILURE(webViewExperimentalEnvironment->CreateCoreWebView2CompositionController( + m_mainWindow, + Callback< + ICoreWebView2ExperimentalCreateCoreWebView2CompositionControllerCompletedHandler>( + [this]( + HRESULT result, + ICoreWebView2ExperimentalCompositionController* compositionController) -> HRESULT { + auto controller = + wil::com_ptr(compositionController) + .query(); + return OnCreateCoreWebView2ControllerCompleted(result, controller.get()); + }) + .Get())); + } + else + { + CHECK_FAILURE(m_webViewEnvironment->CreateCoreWebView2Controller( + m_mainWindow, Callback( + this, &AppWindow::OnCreateCoreWebView2ControllerCompleted) + .Get())); + } + + return S_OK; +} +//! [CreateCoreWebView2Controller] + +// This is the callback passed to CreateCoreWebView2Controller. Here we initialize all WebView-related +// state and register most of our event handlers with the WebView. +HRESULT AppWindow::OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller) +{ + if (result == S_OK) + { + m_controller = controller; + wil::com_ptr coreWebView2; + CHECK_FAILURE(m_controller->get_CoreWebView2(&coreWebView2)); + // We should check for failure here because if this app is using a newer + // SDK version compared to the install of the Edge browser, the Edge + // browser might not have support for the latest version of the + // ICoreWebView2_N interface. + coreWebView2.query_to(&m_webView); + // Create components. These will be deleted when the WebView is closed. + NewComponent(this); + NewComponent(this); + NewComponent(this); + NewComponent( + this, m_webViewEnvironment.get(), m_oldSettingsComponent.get()); + m_oldSettingsComponent = nullptr; + NewComponent( + this, m_dcompDevice.get(), +#ifdef USE_WEBVIEW2_WIN10 + m_wincompCompositor, +#endif + m_creationModeId == IDM_CREATION_MODE_TARGET_DCOMP); + NewComponent(this, &m_toolbar); + + // We have a few of our own event handlers to register here as well + RegisterEventHandlers(); + + // Set the initial size of the WebView + ResizeEverything(); + + if (m_onWebViewFirstInitialized) + { + m_onWebViewFirstInitialized(); + m_onWebViewFirstInitialized = nullptr; + } + + if (m_initialUri.empty()) + { + // StartPage uses initialized values of the WebView and Environment + // so we wait to call StartPage::GetUri until after the WebView is + // created. + m_initialUri = AppStartPage::GetUri(this); + } + + if (m_initialUri != L"none") + { + CHECK_FAILURE(m_webView->Navigate(m_initialUri.c_str())); + } + } + else + { + ShowFailure(result, L"Failed to create webview"); + } + return S_OK; +} +void AppWindow::ReinitializeWebView() +{ + // Save the settings component from being deleted when the WebView is closed, so we can + // copy its properties to the next settings component. + m_oldSettingsComponent = MoveComponent(); + InitializeWebView(); +} + +void AppWindow::ReinitializeWebViewWithNewBrowser() +{ + // Save the settings component from being deleted when the WebView is closed, so we can + // copy its properties to the next settings component. + m_oldSettingsComponent = MoveComponent(); + + // Use the reference to the web view before we close it + UINT webviewProcessId = 0; + m_webView->get_BrowserProcessId(&webviewProcessId); + + // We need to close the current webviews and wait for the browser_process to exit + // This is so the new webviews don't use the old browser exe + CloseWebView(); + + // Make sure the browser process inside webview is closed + ProcessComponent::EnsureProcessIsClosed(webviewProcessId, 2000); + + InitializeWebView(); +} + +void AppWindow::RestartApp() +{ + // Use the reference to the web view before we close the app window + UINT webviewProcessId = 0; + m_webView->get_BrowserProcessId(&webviewProcessId); + + // To restart the app completely, first we close the current App Window + CloseAppWindow(); + + // Make sure the browser process inside webview is closed + ProcessComponent::EnsureProcessIsClosed(webviewProcessId, 2000); + + // Get the command line arguments used to start this app + // so we can re-create the process with them + LPWSTR args = GetCommandLineW(); + + STARTUPINFOW startup_info = {0}; + startup_info.cb = sizeof(startup_info); + PROCESS_INFORMATION temp_process_info = {}; + // Start a new process + if (!::CreateProcess( + nullptr, args, + nullptr, // default process attributes + nullptr, // default thread attributes + FALSE, // do not inherit handles + 0, + nullptr, // no environment + nullptr, // default current directory + &startup_info, &temp_process_info)) + { + // Log some error information if desired + } + + // Terminate this current process + ::exit(0); +} + +void AppWindow::RegisterEventHandlers() +{ + //! [ContainsFullScreenElementChanged] + // Register a handler for the ContainsFullScreenChanged event. + CHECK_FAILURE(m_webView->add_ContainsFullScreenElementChanged( + Callback( + [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT { + if (m_fullScreenAllowed) + { + CHECK_FAILURE( + sender->get_ContainsFullScreenElement(&m_containsFullscreenElement)); + if (m_containsFullscreenElement) + { + EnterFullScreen(); + } + else + { + ExitFullScreen(); + } + } + return S_OK; + }) + .Get(), + nullptr)); + //! [ContainsFullScreenElementChanged] + + //! [NewWindowRequested] + // Register a handler for the NewWindowRequested event. + // This handler will defer the event, create a new app window, and then once the + // new window is ready, it'll provide that new window's WebView as the response to + // the request. + CHECK_FAILURE(m_webView->add_NewWindowRequested( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NewWindowRequestedEventArgs* args) { + wil::com_ptr deferral; + CHECK_FAILURE(args->GetDeferral(&deferral)); + AppWindow* newAppWindow; + + wil::com_ptr windowFeatures; + CHECK_FAILURE(args->get_WindowFeatures(&windowFeatures)); + + RECT windowRect = {0}; + UINT32 left = 0; + UINT32 top = 0; + UINT32 height = 0; + UINT32 width = 0; + BOOL shouldHaveToolbar = true; + + BOOL hasPosition = FALSE; + BOOL hasSize = FALSE; + CHECK_FAILURE(windowFeatures->get_HasPosition(&hasPosition)); + CHECK_FAILURE(windowFeatures->get_HasSize(&hasSize)); + + bool useDefaultWindow = true; + + if (!!hasPosition && !!hasSize) + { + CHECK_FAILURE(windowFeatures->get_Left(&left)); + CHECK_FAILURE(windowFeatures->get_Top(&top)); + CHECK_FAILURE(windowFeatures->get_Height(&height)); + CHECK_FAILURE(windowFeatures->get_Width(&width)); + useDefaultWindow = false; + } + CHECK_FAILURE(windowFeatures->get_ShouldDisplayToolbar(&shouldHaveToolbar)); + + windowRect.left = left; + windowRect.right = left + (width < s_minNewWindowSize ? s_minNewWindowSize : width); + windowRect.top = top; + windowRect.bottom = top + (height < s_minNewWindowSize ? s_minNewWindowSize : height); + + if (!useDefaultWindow) + { + newAppWindow = new AppWindow(m_creationModeId, L"", false, nullptr, true, windowRect, !!shouldHaveToolbar); + } + else + { + newAppWindow = new AppWindow(m_creationModeId, L""); + } + newAppWindow->m_isPopupWindow = true; + newAppWindow->m_onWebViewFirstInitialized = [args, deferral, newAppWindow]() { + CHECK_FAILURE(args->put_NewWindow(newAppWindow->m_webView.get())); + CHECK_FAILURE(args->put_Handled(TRUE)); + CHECK_FAILURE(deferral->Complete()); + }; + + return S_OK; + }) + .Get(), + nullptr)); + //! [NewWindowRequested] + + //! [WindowCloseRequested] + // Register a handler for the WindowCloseRequested event. + // This handler will close the app window if it is not the main window. + CHECK_FAILURE(m_webView->add_WindowCloseRequested( + Callback([this]( + ICoreWebView2* sender, + IUnknown* args) { + if (m_isPopupWindow) + { + CloseAppWindow(); + } + return S_OK; + }).Get(), + nullptr)); + //! [WindowCloseRequested] + + //! [NewBrowserVersionAvailable] + // After the environment is successfully created, + // register a handler for the NewBrowserVersionAvailable event. + // This handler tells when there is a new Edge version available on the machine. + CHECK_FAILURE(m_webViewEnvironment->add_NewBrowserVersionAvailable( + Callback( + [this](ICoreWebView2Environment* sender, IUnknown* args) -> HRESULT { + std::wstring message = L"We detected there is a new version for the browser."; + if (m_webView) + { + message += L"Do you want to restart the app? \n\n"; + message += L"Click No if you only want to re-create the webviews. \n"; + message += L"Click Cancel for no action. \n"; + } + int response = MessageBox( + m_mainWindow, message.c_str(), L"New available version", + m_webView ? MB_YESNOCANCEL : MB_OK); + + if (response == IDYES) + { + RestartApp(); + } + else if (response == IDNO) + { + ReinitializeWebViewWithNewBrowser(); + } + else + { + // do nothing + } + + return S_OK; + }) + .Get(), + nullptr)); + //! [NewBrowserVersionAvailable] +} + +// Updates the sizing and positioning of everything in the window. +void AppWindow::ResizeEverything() +{ + RECT availableBounds = {0}; + GetClientRect(m_mainWindow, &availableBounds); + + if (!m_containsFullscreenElement) + { + availableBounds = m_toolbar.Resize(availableBounds); + } + + if (auto view = GetComponent()) + { + view->SetBounds(availableBounds); + } +} + +//! [Close] +// Close the WebView and deinitialize related state. This doesn't close the app window. +void AppWindow::CloseWebView(bool cleanupUserDataFolder) +{ + DeleteAllComponents(); + if (m_controller) + { + m_controller->Close(); + m_controller = nullptr; + m_webView = nullptr; + } + m_webViewEnvironment = nullptr; + if (cleanupUserDataFolder) + { + // For non-UWP apps, the default user data folder {Executable File Name}.WebView2 + // is in the same directory next to the app executable. If end + // developers specify userDataFolder during WebView environment + // creation, they would need to pass in that explicit value here. + // For more information about userDataFolder: + // https://docs.microsoft.com/microsoft-edge/webview2/reference/win32/webview2-idl#createcorewebview2environmentwithoptions + WCHAR userDataFolder[MAX_PATH] = L""; + // Obtain the absolute path for relative paths that include "./" or "../" + _wfullpath( + userDataFolder, GetLocalPath(L".WebView2", true).c_str(), MAX_PATH); + std::wstring userDataFolderPath(userDataFolder); + + std::wstring message = L"Are you sure you want to clean up the user data folder at\n"; + message += userDataFolderPath; + message += L"\n?\nWarning: This action is not reversible.\n\n"; + message += L"Click No if there are other open WebView instances.\n"; + + if (MessageBox(m_mainWindow, message.c_str(), L"Cleanup User Data Folder", MB_YESNO) == + IDYES) + { + CHECK_FAILURE(DeleteFileRecursive(userDataFolderPath)); + } + } +} +//! [Close] + +HRESULT AppWindow::DeleteFileRecursive(std::wstring path) +{ + wil::com_ptr fileOperation; + CHECK_FAILURE( + CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&fileOperation))); + + // Turn off all UI from being shown to the user during the operation. + CHECK_FAILURE(fileOperation->SetOperationFlags(FOF_NO_UI)); + + wil::com_ptr userDataFolder; + CHECK_FAILURE( + SHCreateItemFromParsingName(path.c_str(), NULL, IID_PPV_ARGS(&userDataFolder))); + + // Add the operation + CHECK_FAILURE(fileOperation->DeleteItem(userDataFolder.get(), NULL)); + CHECK_FAILURE(userDataFolder->Release()); + + // Perform the operation to delete the directory + CHECK_FAILURE(fileOperation->PerformOperations()); + + CHECK_FAILURE(fileOperation->Release()); + CoUninitialize(); + return S_OK; +} + +void AppWindow::CloseAppWindow() +{ + CloseWebView(); + DestroyWindow(m_mainWindow); +} + +void AppWindow::DeleteComponent(ComponentBase* component) +{ + for (auto iter = m_components.begin(); iter != m_components.end(); iter++) + { + if (iter->get() == component) + { + m_components.erase(iter); + return; + } + } +} + +void AppWindow::DeleteAllComponents() +{ + // Delete components in reverse order of initialization. + while (!m_components.empty()) + { + m_components.pop_back(); + } +} + +template std::unique_ptr AppWindow::MoveComponent() +{ + for (auto iter = m_components.begin(); iter != m_components.end(); iter++) + { + if (dynamic_cast(iter->get())) + { + auto wanted = reinterpret_cast&&>(std::move(*iter)); + m_components.erase(iter); + return std::move(wanted); + } + } + return nullptr; +} + +void AppWindow::SetTitleText(PCWSTR titleText) +{ + SetWindowText(m_mainWindow, titleText); +} + +RECT AppWindow::GetWindowBounds() +{ + RECT hwndBounds = {0}; + GetClientRect(m_mainWindow, &hwndBounds); + return hwndBounds; +} + +std::wstring AppWindow::GetLocalPath(std::wstring relativePath, bool keep_exe_path) +{ + WCHAR rawPath[MAX_PATH]; + GetModuleFileNameW(g_hInstance, rawPath, MAX_PATH); + std::wstring path(rawPath); + if (keep_exe_path) + { + path.append(relativePath); + } + else + { + std::size_t index = path.find_last_of(L"\\") + 1; + path.replace(index, path.length(), relativePath); + } + return path; +} +std::wstring AppWindow::GetLocalUri(std::wstring relativePath) +{ +#if 0 // To be enabled after AddHostMappingForLocalFolder fully works. + //! [LocalUrlUsage] + const std::wstring localFileRootUrl = L"https://app-file.invalid/"; + return localFileRootUrl + regex_replace(relativePath, std::wregex(L"\\"), L"/"); + //! [LocalUrlUsage] +#else + std::wstring path = GetLocalPath(relativePath, false); + + wil::com_ptr uri; + CHECK_FAILURE(CreateUri(path.c_str(), Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME, 0, &uri)); + + wil::unique_bstr uriBstr; + CHECK_FAILURE(uri->GetAbsoluteUri(&uriBstr)); + return std::wstring(uriBstr.get()); +#endif +} + +void AppWindow::RunAsync(std::function callback) +{ + auto* task = new std::function(callback); + PostMessage(m_mainWindow, s_runAsyncWindowMessage, reinterpret_cast(task), 0); +} + +void AppWindow::EnterFullScreen() +{ + DWORD style = GetWindowLong(m_mainWindow, GWL_STYLE); + MONITORINFO monitor_info = {sizeof(monitor_info)}; + m_hMenu = ::GetMenu(m_mainWindow); + ::SetMenu(m_mainWindow, nullptr); + if (GetWindowRect(m_mainWindow, &m_previousWindowRect) && + GetMonitorInfo( + MonitorFromWindow(m_mainWindow, MONITOR_DEFAULTTOPRIMARY), &monitor_info)) + { + SetWindowLong(m_mainWindow, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); + SetWindowPos( + m_mainWindow, HWND_TOP, monitor_info.rcMonitor.left, monitor_info.rcMonitor.top, + monitor_info.rcMonitor.right - monitor_info.rcMonitor.left, + monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); + } +} + +void AppWindow::ExitFullScreen() +{ + DWORD style = GetWindowLong(m_mainWindow, GWL_STYLE); + ::SetMenu(m_mainWindow, m_hMenu); + SetWindowLong(m_mainWindow, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); + SetWindowPos( + m_mainWindow, NULL, m_previousWindowRect.left, m_previousWindowRect.top, + m_previousWindowRect.right - m_previousWindowRect.left, + m_previousWindowRect.bottom - m_previousWindowRect.top, + SWP_NOOWNERZORDER | SWP_FRAMECHANGED); +} + +// We have our own implementation of DCompositionCreateDevice2 that dynamically +// loads dcomp.dll to create the device. Not having a static dependency on dcomp.dll +// enables the sample app to run on versions of Windows that don't support dcomp. +HRESULT AppWindow::DCompositionCreateDevice2(IUnknown* renderingDevice, REFIID riid, void** ppv) +{ + HRESULT hr = E_FAIL; + static decltype(::DCompositionCreateDevice2)* fnCreateDCompDevice2 = nullptr; + if (fnCreateDCompDevice2 == nullptr) + { + HMODULE hmod = ::LoadLibraryEx(L"dcomp.dll", nullptr, 0); + if (hmod != nullptr) + { + fnCreateDCompDevice2 = reinterpret_cast( + ::GetProcAddress(hmod, "DCompositionCreateDevice2")); + } + } + if (fnCreateDCompDevice2 != nullptr) + { + hr = fnCreateDCompDevice2(renderingDevice, riid, ppv); + } + return hr; +} + +// WinRT APIs cannot run without a DispatcherQueue. This helper function creates a +// DispatcherQueueController (which instantiates a DispatcherQueue under the covers) that will +// manage tasks for the WinRT APIs. The DispatcherQueue implementation lives in +// CoreMessaging.dll Similar to dcomp.dll, we load CoreMessaging.dll dynamically so the sample +// app can run on versions of windows that don't have CoreMessaging. +HRESULT AppWindow::TryCreateDispatcherQueue() +{ + namespace winSystem = winrt::Windows::System; + + HRESULT hr = S_OK; + thread_local winSystem::DispatcherQueueController dispatcherQueueController{ nullptr }; + + if (dispatcherQueueController == nullptr) + { + hr = E_FAIL; + static decltype(::CreateDispatcherQueueController)* fnCreateDispatcherQueueController = + nullptr; + if (fnCreateDispatcherQueueController == nullptr) + { + HMODULE hmod = ::LoadLibraryEx(L"CoreMessaging.dll", nullptr, 0); + if (hmod != nullptr) + { + fnCreateDispatcherQueueController = + reinterpret_cast( + ::GetProcAddress(hmod, "CreateDispatcherQueueController")); + } + } + if (fnCreateDispatcherQueueController != nullptr) + { + winSystem::DispatcherQueueController controller{ nullptr }; + DispatcherQueueOptions options + { + sizeof(DispatcherQueueOptions), + DQTYPE_THREAD_CURRENT, + DQTAT_COM_STA + }; + hr = fnCreateDispatcherQueueController( + options, reinterpret_cast( + winrt::put_abi(controller))); + dispatcherQueueController = controller; + } + } + + return hr; +} + +#ifdef USE_WEBVIEW2_WIN10 +//! [TextScaleChanged2] +void AppWindow::OnTextScaleChanged( + winrt::Windows::UI::ViewManagement::UISettings const& settings, + winrt::Windows::Foundation::IInspectable const& args) +{ + RunAsync([this] { + m_toolbar.UpdateDpiAndTextScale(); + }); +} +//! [TextScaleChanged2] +#endif +void AppWindow::UpdateCreationModeMenu() +{ + HMENU hMenu = GetMenu(m_mainWindow); + CheckMenuRadioItem( + hMenu, + IDM_CREATION_MODE_WINDOWED, +#ifdef USE_WEBVIEW2_WIN10 + IDM_CREATION_MODE_VISUAL_WINCOMP, +#else + IDM_CREATION_MODE_TARGET_DCOMP, +#endif + m_creationModeId, + MF_BYCOMMAND); +} + +double AppWindow::GetDpiScale() +{ + return DpiUtil::GetDpiForWindow(m_mainWindow) * 1.0f / USER_DEFAULT_SCREEN_DPI; +} + +#ifdef USE_WEBVIEW2_WIN10 +double AppWindow::GetTextScale() +{ + return m_uiSettings ? m_uiSettings.TextScaleFactor() : 1.0f; +} +#endif + +void AppWindow::AddRef() +{ + InterlockedIncrement((LONG *)&m_refCount); +} + +void AppWindow::Release() +{ + uint32_t refCount = InterlockedDecrement((LONG *)&m_refCount); + if (refCount == 0) + { + delete this; + } +} + +void AppWindow::NotifyClosed() +{ + m_isClosed = true; +} + +void AppWindow::InstallComplete(int return_code) +{ + if (!m_isClosed) + { + if (return_code == 0) + { + RunAsync([this] { + InitializeWebView(); + }); + } + else if (return_code == 1) + { + MessageBox(m_mainWindow, L"WebView Runtime failed to Install", L"WebView Runtime Installation status", MB_OK); + } + else if (return_code == 2) + { + MessageBox(m_mainWindow, L"WebView Bootstrapper failled to download", L"WebView Bootstrapper Download status", MB_OK); + } + } +} diff --git a/SampleApps/WebView2APISample/AppWindow.h b/SampleApps/WebView2APISample/AppWindow.h index 32e03242..ed1befcb 100644 --- a/SampleApps/WebView2APISample/AppWindow.h +++ b/SampleApps/WebView2APISample/AppWindow.h @@ -1,171 +1,179 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "stdafx.h" - -#include "ComponentBase.h" -#include "Toolbar.h" -#include "resource.h" -#include -#include -#include -#include -#include -#include -#include -#ifdef USE_WEBVIEW2_WIN10 -#include -#include - -namespace winrtComp = winrt::Windows::UI::Composition; -#endif - -class SettingsComponent; - -class AppWindow -{ -public: - AppWindow( - UINT creationModeId, - std::wstring initialUri = L"https://www.bing.com/", - bool isMainWindow = false, - std::function webviewCreatedCallback = nullptr, - bool customWindowRect = false, - RECT windowRect = { 0 }, - bool shouldHaveToolbar = true); - - ICoreWebView2Controller* GetWebViewController() - { - return m_controller.get(); - } - ICoreWebView2* GetWebView() - { - return m_webView.get(); - } - HWND GetMainWindow() - { - return m_mainWindow; - } - void SetTitleText(PCWSTR titleText); - RECT GetWindowBounds(); - std::wstring GetLocalUri(std::wstring path); - std::function GetAcceleratorKeyFunction(UINT key); - double GetDpiScale(); -#ifdef USE_WEBVIEW2_WIN10 - double GetTextScale(); -#endif - - void ReinitializeWebView(); - - template void NewComponent(Args&&... args); - - template ComponentType* GetComponent(); - - void DeleteComponent(ComponentBase* scenario); - - void RunAsync(std::function callback); - - void InstallComplete(int return_code); - - void AddRef(); - void Release(); - void NotifyClosed(); - -private: - static PCWSTR GetWindowClass(); - - static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); - - static LRESULT CALLBACK - WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - bool HandleWindowMessage( - HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result); - - bool ExecuteWebViewCommands(WPARAM wParam, LPARAM lParam); - bool ExecuteAppCommands(WPARAM wParam, LPARAM lParam); - - void ResizeEverything(); - void InitializeWebView(); - HRESULT OnCreateEnvironmentCompleted(HRESULT result, ICoreWebView2Environment* environment); - HRESULT OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller); - HRESULT DeleteFileRecursive(std::wstring path); - void RegisterEventHandlers(); - void ReinitializeWebViewWithNewBrowser(); - void RestartApp(); - void CloseWebView(bool cleanupUserDataFolder = false); - void CloseAppWindow(); - void ChangeLanguage(); - void UpdateCreationModeMenu(); - void ToggleAADSSO(); -#ifdef USE_WEBVIEW2_WIN10 - void OnTextScaleChanged( - winrt::Windows::UI::ViewManagement::UISettings const& uiSettings, - winrt::Windows::Foundation::IInspectable const& args); -#endif - std::wstring GetLocalPath(std::wstring path, bool keep_exe_path); - void DeleteAllComponents(); - - template std::unique_ptr MoveComponent(); - - std::wstring m_initialUri; - HWND m_mainWindow = nullptr; - Toolbar m_toolbar; - std::function m_onWebViewFirstInitialized; - DWORD m_creationModeId = 0; - int m_refCount = 1; - bool m_isClosed = false; - - // The following is state that belongs with the webview, and should - // be reinitialized along with it. Everything here is undefined when - // m_webView is null. - wil::com_ptr m_webViewEnvironment; - wil::com_ptr m_controller; - wil::com_ptr m_webView; - - // All components are deleted when the WebView is closed. - std::vector> m_components; - std::unique_ptr m_oldSettingsComponent; - - std::wstring m_language; - - bool m_AADSSOEnabled = false; - - // Fullscreen related code - RECT m_previousWindowRect; - HMENU m_hMenu; - BOOL m_containsFullscreenElement = FALSE; - bool m_fullScreenAllowed = true; - bool m_isPopupWindow = false; - void EnterFullScreen(); - void ExitFullScreen(); - - // Compositor creation helper methods - HRESULT DCompositionCreateDevice2(IUnknown* renderingDevice, REFIID riid, void** ppv); - HRESULT TryCreateDispatcherQueue(); - - wil::com_ptr m_dcompDevice; -#ifdef USE_WEBVIEW2_WIN10 - winrtComp::Compositor m_wincompCompositor{ nullptr }; - winrt::Windows::UI::ViewManagement::UISettings m_uiSettings{ nullptr }; -#endif -}; - -template void AppWindow::NewComponent(Args&&... args) -{ - m_components.emplace_back(new ComponentType(std::forward(args)...)); -} - -template ComponentType* AppWindow::GetComponent() -{ - for (auto& component : m_components) - { - if (auto wanted = dynamic_cast(component.get())) - { - return wanted; - } - } - return nullptr; -} +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "stdafx.h" + +#include "ComponentBase.h" +#include "Toolbar.h" +#include "resource.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_WEBVIEW2_WIN10 +#include +#include + +namespace winrtComp = winrt::Windows::UI::Composition; +#endif + +class SettingsComponent; + +class AppWindow +{ +public: + AppWindow( + UINT creationModeId, + std::wstring initialUri = L"", + bool isMainWindow = false, + std::function webviewCreatedCallback = nullptr, + bool customWindowRect = false, + RECT windowRect = { 0 }, + bool shouldHaveToolbar = true); + + ICoreWebView2Controller* GetWebViewController() + { + return m_controller.get(); + } + ICoreWebView2* GetWebView() + { + return m_webView.get(); + } + ICoreWebView2Environment* GetWebViewEnvironment() + { + return m_webViewEnvironment.get(); + } + HWND GetMainWindow() + { + return m_mainWindow; + } + void SetTitleText(PCWSTR titleText); + RECT GetWindowBounds(); + std::wstring GetLocalUri(std::wstring path); + std::function GetAcceleratorKeyFunction(UINT key); + double GetDpiScale(); +#ifdef USE_WEBVIEW2_WIN10 + double GetTextScale(); +#endif + + void ReinitializeWebView(); + + template void NewComponent(Args&&... args); + + template ComponentType* GetComponent(); + + void DeleteComponent(ComponentBase* scenario); + + void RunAsync(std::function callback); + + void InstallComplete(int return_code); + + void AddRef(); + void Release(); + void NotifyClosed(); + +private: + static PCWSTR GetWindowClass(); + + static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); + + static LRESULT CALLBACK + WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + bool HandleWindowMessage( + HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result); + + bool ExecuteWebViewCommands(WPARAM wParam, LPARAM lParam); + bool ExecuteAppCommands(WPARAM wParam, LPARAM lParam); + + void ResizeEverything(); + void InitializeWebView(); + HRESULT OnCreateEnvironmentCompleted(HRESULT result, ICoreWebView2Environment* environment); + HRESULT OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller); + HRESULT DeleteFileRecursive(std::wstring path); + void RegisterEventHandlers(); + void ReinitializeWebViewWithNewBrowser(); + void RestartApp(); + void CloseWebView(bool cleanupUserDataFolder = false); + void CloseAppWindow(); + void ChangeLanguage(); + void UpdateCreationModeMenu(); + void ToggleAADSSO(); +#ifdef USE_WEBVIEW2_WIN10 + void OnTextScaleChanged( + winrt::Windows::UI::ViewManagement::UISettings const& uiSettings, + winrt::Windows::Foundation::IInspectable const& args); +#endif + std::wstring GetLocalPath(std::wstring path, bool keep_exe_path); + void DeleteAllComponents(); + + template std::unique_ptr MoveComponent(); + + // The initial URI to which to navigate the WebView2's top level document. + // This is either empty string in which case we will use StartPage::GetUri, + // or "none" to mean don't perform an initial navigate, + // or a valid absolute URI to which we will navigate. + std::wstring m_initialUri; + HWND m_mainWindow = nullptr; + Toolbar m_toolbar; + std::function m_onWebViewFirstInitialized; + DWORD m_creationModeId = 0; + int m_refCount = 1; + bool m_isClosed = false; + + // The following is state that belongs with the webview, and should + // be reinitialized along with it. Everything here is undefined when + // m_webView is null. + wil::com_ptr m_webViewEnvironment; + wil::com_ptr m_controller; + wil::com_ptr m_webView; + + // All components are deleted when the WebView is closed. + std::vector> m_components; + std::unique_ptr m_oldSettingsComponent; + + std::wstring m_language; + + bool m_AADSSOEnabled = false; + + // Fullscreen related code + RECT m_previousWindowRect; + HMENU m_hMenu; + BOOL m_containsFullscreenElement = FALSE; + bool m_fullScreenAllowed = true; + bool m_isPopupWindow = false; + void EnterFullScreen(); + void ExitFullScreen(); + + // Compositor creation helper methods + HRESULT DCompositionCreateDevice2(IUnknown* renderingDevice, REFIID riid, void** ppv); + HRESULT TryCreateDispatcherQueue(); + + wil::com_ptr m_dcompDevice; +#ifdef USE_WEBVIEW2_WIN10 + winrtComp::Compositor m_wincompCompositor{ nullptr }; + winrt::Windows::UI::ViewManagement::UISettings m_uiSettings{ nullptr }; +#endif +}; + +template void AppWindow::NewComponent(Args&&... args) +{ + m_components.emplace_back(new ComponentType(std::forward(args)...)); +} + +template ComponentType* AppWindow::GetComponent() +{ + for (auto& component : m_components) + { + if (auto wanted = dynamic_cast(component.get())) + { + return wanted; + } + } + return nullptr; +} diff --git a/SampleApps/WebView2APISample/DCompTargetImpl.cpp b/SampleApps/WebView2APISample/DCompTargetImpl.cpp index 5a8e91bd..f14b1900 100644 --- a/SampleApps/WebView2APISample/DCompTargetImpl.cpp +++ b/SampleApps/WebView2APISample/DCompTargetImpl.cpp @@ -1,32 +1,32 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "stdafx.h" - -#include "DCompTargetImpl.h" - -DCompTargetImpl::DCompTargetImpl(ViewComponent* owner) -{ - m_viewComponentOwner = owner; -} - -void DCompTargetImpl::RemoveOwnerRef() -{ - m_viewComponentOwner = nullptr; -} - -HRESULT __stdcall DCompTargetImpl::SetRoot(IDCompositionVisual* visual) -{ - HRESULT hr = S_OK; - if (m_viewComponentOwner) - { - hr = m_viewComponentOwner->m_dcompWebViewVisual->RemoveAllVisuals(); - if (SUCCEEDED(hr) && visual) - { - hr = m_viewComponentOwner->m_dcompWebViewVisual->AddVisual(visual, FALSE, nullptr); - } - } - - return hr; +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "DCompTargetImpl.h" + +DCompTargetImpl::DCompTargetImpl(ViewComponent* owner) +{ + m_viewComponentOwner = owner; +} + +void DCompTargetImpl::RemoveOwnerRef() +{ + m_viewComponentOwner = nullptr; +} + +HRESULT __stdcall DCompTargetImpl::SetRoot(IDCompositionVisual* visual) +{ + HRESULT hr = S_OK; + if (m_viewComponentOwner) + { + hr = m_viewComponentOwner->m_dcompWebViewVisual->RemoveAllVisuals(); + if (SUCCEEDED(hr) && visual) + { + hr = m_viewComponentOwner->m_dcompWebViewVisual->AddVisual(visual, FALSE, nullptr); + } + } + + return hr; } \ No newline at end of file diff --git a/SampleApps/WebView2APISample/DCompTargetImpl.h b/SampleApps/WebView2APISample/DCompTargetImpl.h index a05bf578..f21e6d03 100644 --- a/SampleApps/WebView2APISample/DCompTargetImpl.h +++ b/SampleApps/WebView2APISample/DCompTargetImpl.h @@ -1,24 +1,24 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "stdafx.h" - -#include "ViewComponent.h" - -class DCompTargetImpl - : public Microsoft::WRL::RuntimeClass< - Microsoft::WRL::RuntimeClassFlags, IDCompositionTarget> -{ -public: - DCompTargetImpl(ViewComponent* owner); - void RemoveOwnerRef(); - - // Inherited via IDCompositionTarget - virtual HRESULT __stdcall SetRoot(IDCompositionVisual* visual) override; - -private: - ViewComponent* m_viewComponentOwner; -}; +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "stdafx.h" + +#include "ViewComponent.h" + +class DCompTargetImpl + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags, IDCompositionTarget> +{ +public: + DCompTargetImpl(ViewComponent* owner); + void RemoveOwnerRef(); + + // Inherited via IDCompositionTarget + virtual HRESULT __stdcall SetRoot(IDCompositionVisual* visual) override; + +private: + ViewComponent* m_viewComponentOwner; +}; diff --git a/SampleApps/WebView2APISample/ScenarioAddHostObject.html b/SampleApps/WebView2APISample/ScenarioAddHostObject.html index 4d522276..9ea09f92 100644 --- a/SampleApps/WebView2APISample/ScenarioAddHostObject.html +++ b/SampleApps/WebView2APISample/ScenarioAddHostObject.html @@ -1,121 +1,121 @@ - - - - AddHostObjectToScript Sample - - -

AddHostObjectToScript Sample

-

The following buttons interact with the chrome.webview.hostObjects.sample object. Open DevTools console to try running whatever code you like on this object.

-

Get Property

- - -
- - - -
- -

Set Property

- - -
- - - -
- - - -
- -

Indexed Property

- - -
- - - -
- -

Invoke Method

- - -
- - - -
- -

Invoke Callback

- - -
- - - + + + + AddHostObjectToScript Sample + + +

AddHostObjectToScript Sample

+

The following buttons interact with the chrome.webview.hostObjects.sample object. Open DevTools console to try running whatever code you like on this object.

+

Get Property

+ + +
+ + + +
+ +

Set Property

+ + +
+ + + +
+ + + +
+ +

Indexed Property

+ + +
+ + + +
+ +

Invoke Method

+ + +
+ + + +
+ +

Invoke Callback

+ + +
+ + + \ No newline at end of file diff --git a/SampleApps/WebView2APISample/ScenarioCookieManagement.cpp b/SampleApps/WebView2APISample/ScenarioCookieManagement.cpp new file mode 100644 index 00000000..ad01a435 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioCookieManagement.cpp @@ -0,0 +1,227 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "ScenarioCookieManagement.h" + +#include "AppWindow.h" +#include "CheckFailure.h" +#include +#include + +using namespace Microsoft::WRL; + +static constexpr WCHAR c_samplePath[] = L"ScenarioCookieManagement.html"; + +ScenarioCookieManagement::ScenarioCookieManagement(AppWindow* appWindow) + : m_appWindow(appWindow), m_webView(appWindow->GetWebView()) +{ + m_sampleUri = m_appWindow->GetLocalUri(c_samplePath); + + ComPtr settings; + CHECK_FAILURE(m_webView->get_Settings(&settings)); + CHECK_FAILURE(settings->put_IsWebMessageEnabled(TRUE)); + + //! [CookieManager] + m_webViewExperimental = m_webView.try_query(); + CHECK_FAILURE(m_webViewExperimental->get_CookieManager(&m_cookieManager)); + //! [CookieManager] + + // Setup the web message received event handler before navigating to + // ensure we don't miss any messages. + CHECK_FAILURE(m_webView->add_WebMessageReceived( + Microsoft::WRL::Callback( + [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) { + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(args->get_Source(&uri)); + + // Always validate that the origin of the message is what you expect. + if (uri.get() != m_sampleUri) + { + return S_OK; + } + wil::unique_cotaskmem_string messageRaw; + CHECK_FAILURE(args->TryGetWebMessageAsString(&messageRaw)); + std::wstring message = messageRaw.get(); + std::wstring reply; + + if (message.compare(0, 11, L"GetCookies ") == 0) + { + std::wstring uri; + if (message.length() != 11) + { + uri = message.substr(11); + } + GetCookiesHelper(uri.c_str()); + } + else if (message.compare(0, 17, L"AddOrUpdateCookie") == 0) + { + //! [AddOrUpdateCookie] + wil::com_ptr cookie; + CHECK_FAILURE(m_cookieManager->CreateCookie( + L"CookieName", L"CookieValue", L".bing.com", L"/", &cookie)); + CHECK_FAILURE(m_cookieManager->AddOrUpdateCookie(cookie.get())); + //! [AddOrUpdateCookie] + } + else if (message.compare(0, 16, L"DeleteAllCookies") == 0) + { + CHECK_FAILURE(m_cookieManager->DeleteAllCookies()); + } + return S_OK; + }) + .Get(), + &m_webMessageReceivedToken)); + + // Turn off this scenario if we navigate away from the sample page + CHECK_FAILURE(m_webView->add_ContentLoading( + Callback( + [this]( + ICoreWebView2* sender, ICoreWebView2ContentLoadingEventArgs* args) -> HRESULT { + wil::unique_cotaskmem_string uri; + sender->get_Source(&uri); + if (uri.get() != m_sampleUri) + { + m_appWindow->DeleteComponent(this); + } + return S_OK; + }) + .Get(), + &m_contentLoadingToken)); + + CHECK_FAILURE(m_webView->Navigate(m_sampleUri.c_str())); +} + +ScenarioCookieManagement::~ScenarioCookieManagement() +{ + m_webView->remove_WebMessageReceived(m_webMessageReceivedToken); + m_webView->remove_ContentLoading(m_contentLoadingToken); +} + +static std::wstring BoolToString(BOOL value) +{ + return value ? L"true" : L"false"; +} + +static std::wstring EncodeQuote(std::wstring raw) +{ + return L"\"" + regex_replace(raw, std::wregex(L"\""), L"\\\"") + L"\""; +} + +static std::wstring SecondsToString(UINT32 time) +{ + WCHAR rawResult[26]; + time_t rawTime; + rawTime = (const time_t)time; + struct tm timeStruct; + gmtime_s(&timeStruct, &rawTime); + _wasctime_s(rawResult, 26, &timeStruct); + std::wstring result(rawResult); + return result; +} + +static std::wstring CookieToString(ICoreWebView2ExperimentalCookie* cookie) +{ + //! [CookieObject] + wil::unique_cotaskmem_string name; + CHECK_FAILURE(cookie->get_Name(&name)); + wil::unique_cotaskmem_string value; + CHECK_FAILURE(cookie->get_Value(&value)); + wil::unique_cotaskmem_string domain; + CHECK_FAILURE(cookie->get_Domain(&domain)); + wil::unique_cotaskmem_string path; + CHECK_FAILURE(cookie->get_Path(&path)); + double expires; + CHECK_FAILURE(cookie->get_Expires(&expires)); + BOOL isHttpOnly = FALSE; + CHECK_FAILURE(cookie->get_IsHttpOnly(&isHttpOnly)); + COREWEBVIEW2_COOKIE_SAME_SITE_KIND same_site; + std::wstring same_site_as_string; + CHECK_FAILURE(cookie->get_SameSite(&same_site)); + switch (same_site) + { + case COREWEBVIEW2_COOKIE_SAME_SITE_KIND_NONE: + same_site_as_string = L"None"; + break; + case COREWEBVIEW2_COOKIE_SAME_SITE_KIND_LAX: + same_site_as_string = L"Lax"; + break; + case COREWEBVIEW2_COOKIE_SAME_SITE_KIND_STRICT: + same_site_as_string = L"Strict"; + break; + } + BOOL isSecure = FALSE; + CHECK_FAILURE(cookie->get_IsSecure(&isSecure)); + BOOL isSession = FALSE; + CHECK_FAILURE(cookie->get_IsSession(&isSession)); + + std::wstring result = L"{"; + result += L"\"Name\": " + EncodeQuote(name.get()) + L", " + L"\"Value\": " + + EncodeQuote(value.get()) + L", " + L"\"Domain\": " + EncodeQuote(domain.get()) + + L", " + L"\"Path\": " + EncodeQuote(path.get()) + L", " + L"\"HttpOnly\": " + + BoolToString(isHttpOnly) + L", " + L"\"Secure\": " + BoolToString(isSecure) + L", " + + L"\"SameSite\": " + EncodeQuote(same_site_as_string) + L", " + L"\"Expires\": "; + if (!!isSession) + { + result += L"This is a session cookie."; + } + else + { + result += std::to_wstring(expires); + } + + return result + L"\"}"; + //! [CookieObject] +} + +void ScenarioCookieManagement::GetCookiesHelper(std::wstring uri) +{ + //! [GetCookies] + if (m_cookieManager) + { + CHECK_FAILURE(m_cookieManager->GetCookies( + uri.c_str(), + Callback( + [this, uri](HRESULT error_code, ICoreWebView2ExperimentalCookieList* list) -> HRESULT { + CHECK_FAILURE(error_code); + + std::wstring result; + UINT cookie_list_size; + CHECK_FAILURE(list->get_Count(&cookie_list_size)); + + if (cookie_list_size == 0) + { + result += L"No cookies found."; + } + else + { + result += std::to_wstring(cookie_list_size) + L" cookie(s) found"; + if (!uri.empty()) + { + result += L" on " + uri; + } + result += L"\n\n["; + for (int i = 0; i < cookie_list_size; ++i) + { + wil::com_ptr cookie; + CHECK_FAILURE(list->GetValueAtIndex(i, &cookie)); + + if (cookie.get()) + { + result += CookieToString(cookie.get()); + if (i != cookie_list_size - 1) + { + result += L",\n"; + } + } + } + result += L"]"; + } + MessageBox(nullptr, result.c_str(), L"GetCookies Result", MB_OK); + return S_OK; + }) + .Get())); + } + //! [GetCookies] +} diff --git a/SampleApps/WebView2APISample/ScenarioCookieManagement.h b/SampleApps/WebView2APISample/ScenarioCookieManagement.h new file mode 100644 index 00000000..de869f63 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioCookieManagement.h @@ -0,0 +1,30 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once +#include "stdafx.h" + +#include + +#include "AppWindow.h" +#include "ComponentBase.h" + +class ScenarioCookieManagement : public ComponentBase +{ +public: + ScenarioCookieManagement(AppWindow* appWindow); + ~ScenarioCookieManagement() override; + +private: + void GetCookiesHelper(std::wstring uri); + + AppWindow* m_appWindow; + wil::com_ptr m_webViewEnvironment; + wil::com_ptr m_webView; + wil::com_ptr m_webViewExperimental; + wil::com_ptr m_cookieManager; + std::wstring m_sampleUri; + EventRegistrationToken m_webMessageReceivedToken = {}; + EventRegistrationToken m_contentLoadingToken = {}; +}; diff --git a/SampleApps/WebView2APISample/ScenarioCookieManagement.html b/SampleApps/WebView2APISample/ScenarioCookieManagement.html new file mode 100644 index 00000000..6dfc4327 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioCookieManagement.html @@ -0,0 +1,62 @@ + + + + ScenarioCookieManagement + + + +

Cookie Management sample page

+

This page demonstrates basic cookie management.

+ +

Setup

+

+ Press ctrl+t to open another WebView under the same WebView2 environment that navigates to https://www.bing.com. +

+

Creating Cookie Manager

+

+ One can start off by getting the cookie manager associated with the WebView2 by using + ICoreWebView2Experimental::get_CookieManager. +

+ +

Getting Cookies

+

+ One can use the cookie manager to get an ICoreWebView2ExperimentalCookieList + that contains the ICoreWebView2ExperimentalCookies that are associated with the specified URI. + Try clicking the Get cookies button below. If calling GetCookies with an empty URI, all cookies under the same profile are + returned. +

+ + + + + +
+ +

Adding or Updating Cookie

+

One can set a cookie by first creating a cookie object calling ICoreWebView2ExperimentalCookieManager::CreateCookie.

+ + + + +
+ +

Clearing Cookies

+ + + + + diff --git a/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.cpp b/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.cpp new file mode 100644 index 00000000..64c6b0d1 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.cpp @@ -0,0 +1,64 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "ScenarioDOMContentLoaded.h" + +#include "AppWindow.h" +#include "CheckFailure.h" + +using namespace Microsoft::WRL; + +static constexpr WCHAR c_samplePath[] = L"ScenarioDOMContentLoaded.html"; +ScenarioDOMContentLoaded::ScenarioDOMContentLoaded(AppWindow* appWindow) + : m_appWindow(appWindow), m_webView(appWindow->GetWebView()) +{ + m_sampleUri = m_appWindow->GetLocalUri(c_samplePath); + //! [DOMContentLoaded] + // Register a handler for the DOMContentLoaded event. + // Check whether the DOM content loaded + m_webViewExperimental = m_webView.query(); + CHECK_FAILURE(m_webViewExperimental->add_DOMContentLoaded( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2ExperimentalDOMContentLoadedEventArgs* args) + -> HRESULT { + m_webView->ExecuteScript( + L"let " + L"content=document.createElement(\"h2\");content.style.color='blue';" + L"content.textContent=\"This text was added by the host " + L"app\";document.body.appendChild(content);", + Callback( + [](HRESULT error, PCWSTR result) -> HRESULT { return S_OK; }) + .Get()); + return S_OK; + }) + .Get(), + &m_DOMContentLoadedToken)); + //! [DOMContentLoaded] + + // Turn off this scenario if we navigate away from the sample page + CHECK_FAILURE(m_webView->add_ContentLoading( + Callback( + [this]( + ICoreWebView2* sender, ICoreWebView2ContentLoadingEventArgs* args) -> HRESULT { + wil::unique_cotaskmem_string uri; + sender->get_Source(&uri); + if (uri.get() != m_sampleUri) + { + m_appWindow->DeleteComponent(this); + } + return S_OK; + }) + .Get(), + &m_contentLoadingToken)); + + CHECK_FAILURE(m_webView->Navigate(m_sampleUri.c_str())); +} + +ScenarioDOMContentLoaded::~ScenarioDOMContentLoaded() +{ + m_webViewExperimental->remove_DOMContentLoaded(m_DOMContentLoadedToken); + m_webView->remove_ContentLoading(m_contentLoadingToken); +} diff --git a/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.h b/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.h new file mode 100644 index 00000000..eebf6cbd --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.h @@ -0,0 +1,26 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once +#include "stdafx.h" + +#include + +#include "AppWindow.h" +#include "ComponentBase.h" + +class ScenarioDOMContentLoaded : public ComponentBase +{ +public: + ScenarioDOMContentLoaded(AppWindow* appWindow); + ~ScenarioDOMContentLoaded() override; + +private: + AppWindow* m_appWindow = nullptr; + wil::com_ptr m_webView; + wil::com_ptr m_webViewExperimental; + std::wstring m_sampleUri; + EventRegistrationToken m_DOMContentLoadedToken = {}; + EventRegistrationToken m_contentLoadingToken = {}; +}; diff --git a/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.html b/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.html new file mode 100644 index 00000000..2f4165c6 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioDOMContentLoaded.html @@ -0,0 +1,12 @@ + + + + ScenarioDOMContentLoaded + + +

DOMContentLoaded sample page

+ +

The content below will be added after DOM content is loaded

+ + + diff --git a/SampleApps/WebView2APISample/ScenarioNavigateWithWebResourceRequest.cpp b/SampleApps/WebView2APISample/ScenarioNavigateWithWebResourceRequest.cpp new file mode 100644 index 00000000..42dbd56d --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioNavigateWithWebResourceRequest.cpp @@ -0,0 +1,55 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "stdafx.h" +#include "ScenarioNavigateWithWebResourceRequest.h" +#include "AppWindow.h" +#include "CheckFailure.h" +#include "TextInputDialog.h" + +#include + +#include + +using namespace Microsoft::WRL; + +ScenarioNavigateWithWebResourceRequest::ScenarioNavigateWithWebResourceRequest( + AppWindow* appWindow) +{ + // Prepare post data as UTF-8 byte array and convert it to stream + // as required by the application/x-www-form-urlencoded Content-Type + TextInputDialog dialog( + appWindow->GetMainWindow(), L"Post data", L"Post data:", + L"Specify post data to submit to https://www.w3schools.com/action_page.php", + L""); + if (dialog.confirmed) + { + std::wstring postData = std::wstring(L"input=") + dialog.input; + int sizeNeededForMultiByte = WideCharToMultiByte( + CP_UTF8, 0, postData.c_str(), postData.size(), nullptr, + 0, + nullptr, nullptr); + + std::unique_ptr postDataBytes = std::make_unique(sizeNeededForMultiByte); + WideCharToMultiByte( + CP_UTF8, 0, postData.c_str(), postData.size(), postDataBytes.get(), + sizeNeededForMultiByte, nullptr, nullptr); + + //! [NavigateWithWebResourceRequest] + wil::com_ptr webviewExperimental; + CHECK_FAILURE(appWindow->GetWebView()->QueryInterface(IID_PPV_ARGS(&webviewExperimental))); + wil::com_ptr webviewEnvironmentExperimental; + CHECK_FAILURE(appWindow->GetWebViewEnvironment()->QueryInterface( + IID_PPV_ARGS(&webviewEnvironmentExperimental))); + wil::com_ptr webResourceRequest; + wil::com_ptr postDataStream = SHCreateMemStream( + reinterpret_cast(postDataBytes.get()), sizeNeededForMultiByte); + + // This is acts as a form submit to https://www.w3schools.com/action_page.php + CHECK_FAILURE(webviewEnvironmentExperimental->CreateWebResourceRequest( + L"https://www.w3schools.com/action_page.php", L"POST", postDataStream.get(), + L"Content-Type: application/x-www-form-urlencoded", &webResourceRequest)); + CHECK_FAILURE(webviewExperimental->NavigateWithWebResourceRequest(webResourceRequest.get())); + //! [NavigateWithWebResourceRequest] + } +} \ No newline at end of file diff --git a/SampleApps/WebView2APISample/ScenarioNavigateWithWebResourceRequest.h b/SampleApps/WebView2APISample/ScenarioNavigateWithWebResourceRequest.h new file mode 100644 index 00000000..6df3d24e --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioNavigateWithWebResourceRequest.h @@ -0,0 +1,19 @@ +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once +#include "stdafx.h" +#include + +#include "AppWindow.h" +#include "ComponentBase.h" + +class ScenarioNavigateWithWebResourceRequest : public ComponentBase +{ +public: + ScenarioNavigateWithWebResourceRequest(AppWindow* appWindow); + +private: + AppWindow* m_appWindow; +}; \ No newline at end of file diff --git a/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp b/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp index 9f8780f6..66cb2f23 100644 --- a/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp +++ b/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp @@ -1,666 +1,691 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "stdafx.h" - -#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1 - -#include "AppWindow.h" -#include "CheckFailure.h" -#include "ScenarioWebViewEventMonitor.h" -#include -#include -#include -#include -#include - -using namespace Microsoft::WRL; -using namespace std; - -static constexpr wchar_t c_samplePath[] = L"ScenarioWebViewEventMonitor.html"; - -ScenarioWebViewEventMonitor::ScenarioWebViewEventMonitor(AppWindow* appWindowEventSource) - : m_appWindowEventSource(appWindowEventSource), - m_webviewEventSource(appWindowEventSource->GetWebView()) -{ - m_sampleUri = m_appWindowEventSource->GetLocalUri(c_samplePath); - m_appWindowEventView = new AppWindow( - IDM_CREATION_MODE_WINDOWED, - m_sampleUri, - false, - [this]() -> void { - InitializeEventView(m_appWindowEventView->GetWebView()); - }); - m_webviewEventSourceExperimental = m_webviewEventSource.query(); -} - -ScenarioWebViewEventMonitor::~ScenarioWebViewEventMonitor() -{ - m_webviewEventSource->remove_NavigationStarting(m_navigationStartingToken); - m_webviewEventSource->remove_SourceChanged(m_sourceChangedToken); - m_webviewEventSource->remove_ContentLoading(m_contentLoadingToken); - m_webviewEventSource->remove_HistoryChanged(m_historyChangedToken); - m_webviewEventSource->remove_NavigationCompleted(m_navigationCompletedToken); - m_webviewEventSource->remove_DocumentTitleChanged(m_documentTitleChangedToken); - m_webviewEventSource->remove_WebMessageReceived(m_webMessageReceivedToken); - m_webviewEventSource->remove_NewWindowRequested(m_newWindowRequestedToken); - EnableWebResourceRequestedEvent(false); - EnableWebResourceResponseReceivedEvent(false); - - m_webviewEventView->remove_WebMessageReceived(m_eventViewWebMessageReceivedToken); -} - -std::wstring WebErrorStatusToString(COREWEBVIEW2_WEB_ERROR_STATUS status) -{ - switch (status) - { -#define STATUS_ENTRY(statusValue) \ - case statusValue: \ - return L#statusValue; - - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_EXPIRED); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CLIENT_CERTIFICATE_CONTAINS_ERRORS); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_REVOKED); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_SERVER_UNREACHABLE); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_TIMEOUT); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_ERROR_HTTP_INVALID_SERVER_RESPONSE); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_RESET); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CANNOT_CONNECT); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_HOST_NAME_NOT_RESOLVED); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_REDIRECT_FAILED); - STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_UNEXPECTED_ERROR); - -#undef STATUS_ENTRY - } - - return L"ERROR"; -} - -std::wstring BoolToString(BOOL value) -{ - return value ? L"true" : L"false"; -} - -std::wstring EncodeQuote(std::wstring raw) -{ - std::wstring encoded; - // Allocate 10 more chars to reduce memory re-allocation - // due to adding potential escaping chars. - encoded.reserve(raw.length() + 10); - encoded.push_back(L'"'); - for (int i = 0; i < raw.length(); ++i) - { - // Escape chars as listed in https://tc39.es/ecma262/#sec-json.stringify. - switch (raw[i]) - { - case '\b': - encoded.append(L"\\b"); - break; - case '\f': - encoded.append(L"\\f"); - break; - case '\n': - encoded.append(L"\\n"); - break; - case '\r': - encoded.append(L"\\r"); - break; - case '\t': - encoded.append(L"\\t"); - break; - case '\\': - encoded.append(L"\\\\"); - break; - case '"': - encoded.append(L"\\\""); - break; - default: - encoded.push_back(raw[i]); - } - } - encoded.push_back(L'"'); - return encoded; -} - -//! [HttpRequestHeaderIterator] -std::wstring RequestHeadersToJsonString(ICoreWebView2HttpRequestHeaders* requestHeaders) -{ - wil::com_ptr iterator; - CHECK_FAILURE(requestHeaders->GetIterator(&iterator)); - BOOL hasCurrent = FALSE; - std::wstring result = L"["; - - while (SUCCEEDED(iterator->get_HasCurrentHeader(&hasCurrent)) && hasCurrent) - { - wil::unique_cotaskmem_string name; - wil::unique_cotaskmem_string value; - - CHECK_FAILURE(iterator->GetCurrentHeader(&name, &value)); - result += L"{\"name\": " + EncodeQuote(name.get()) - + L", \"value\": " + EncodeQuote(value.get()) + L"}"; - - BOOL hasNext = FALSE; - CHECK_FAILURE(iterator->MoveNext(&hasNext)); - if (hasNext) - { - result += L", "; - } - } - - return result + L"]"; -} -//! [HttpRequestHeaderIterator] - -std::wstring ResponseHeadersToJsonString(ICoreWebView2HttpResponseHeaders* responseHeaders) -{ - wil::com_ptr iterator; - CHECK_FAILURE(responseHeaders->GetIterator(&iterator)); - BOOL hasCurrent = FALSE; - std::wstring result = L"["; - - while (SUCCEEDED(iterator->get_HasCurrentHeader(&hasCurrent)) && hasCurrent) - { - wil::unique_cotaskmem_string name; - wil::unique_cotaskmem_string value; - - CHECK_FAILURE(iterator->GetCurrentHeader(&name, &value)); - result += EncodeQuote(std::wstring(name.get()) + L": " + value.get()); - - BOOL hasNext = FALSE; - CHECK_FAILURE(iterator->MoveNext(&hasNext)); - if (hasNext) - { - result += L", "; - } - } - - return result + L"]"; -} - -std::wstring RequestToJsonString(ICoreWebView2WebResourceRequest* request) -{ - wil::com_ptr content; - CHECK_FAILURE(request->get_Content(&content)); - wil::com_ptr headers; - CHECK_FAILURE(request->get_Headers(&headers)); - wil::unique_cotaskmem_string method; - CHECK_FAILURE(request->get_Method(&method)); - wil::unique_cotaskmem_string uri; - CHECK_FAILURE(request->get_Uri(&uri)); - - std::wstring result = L"{"; - - result += L"\"content\": "; - result += (content == nullptr ? L"null" : L"\"...\""); - result += L", "; - - result += L"\"headers\": " + RequestHeadersToJsonString(headers.get()) + L", "; - result += L"\"method\": " + EncodeQuote(method.get()) + L", "; - result += L"\"uri\": " + EncodeQuote(uri.get()) + L" "; - - result += L"}"; - - return result; -} - -std::wstring GetPreviewOfContent(IStream* content, bool& readAll) -{ - char buffer[50]; - unsigned long read; - content->Read(buffer, 50U, &read); - readAll = read < 50; - - WCHAR converted[50]; - CHECK_FAILURE(MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, buffer, 50, converted, 50)); - return std::wstring(converted); -} - -std::wstring ResponseToJsonString(ICoreWebView2WebResourceResponse* response) -{ - wil::com_ptr content; - CHECK_FAILURE(response->get_Content(&content)); - wil::com_ptr headers; - CHECK_FAILURE(response->get_Headers(&headers)); - int statusCode; - CHECK_FAILURE(response->get_StatusCode(&statusCode)); - wil::unique_cotaskmem_string reasonPhrase; - CHECK_FAILURE(response->get_ReasonPhrase(&reasonPhrase)); - BOOL containsContentType = FALSE; - headers->Contains(L"Content-Type", &containsContentType); - wil::unique_cotaskmem_string contentType; - bool isBinaryContent = true; - if (containsContentType) - { - headers->GetHeader(L"Content-Type", &contentType); - if (wcsncmp(L"text/", contentType.get(), ARRAYSIZE(L"text/")) == 0) - { - isBinaryContent = false; - } - } - std::wstring result = L"{"; - - result += L"\"content\": "; - if (!content) - { - result += L"null"; - } - else - { - if (isBinaryContent) - { - result += EncodeQuote(L"BINARY_DATA"); - } - else - { - bool readAll = false; - result += EncodeQuote(GetPreviewOfContent(content.get(), readAll)); - if (!readAll) - { - result += L"..."; - } - } - } - result += L", "; - - result += L"\"headers\": " + ResponseHeadersToJsonString(headers.get()) + L", "; - result += L"\"status\": "; - WCHAR statusCodeString[4]; - _itow_s(statusCode, statusCodeString, 4, 10); - result += statusCodeString; - result += L", "; - result += L"\"reason\": " + EncodeQuote(reasonPhrase.get()) + L" "; - - result += L"}"; - - return result; -} - -std::wstring WebViewPropertiesToJsonString(ICoreWebView2* webview) -{ - wil::unique_cotaskmem_string documentTitle; - CHECK_FAILURE(webview->get_DocumentTitle(&documentTitle)); - wil::unique_cotaskmem_string source; - CHECK_FAILURE(webview->get_Source(&source)); - - std::wstring result = L", \"webview\": {" - L"\"documentTitle\": " + EncodeQuote(documentTitle.get()) + L", " - + L"\"source\": " + EncodeQuote(source.get()) + L" " - + L"}"; - - return result; -} - -void ScenarioWebViewEventMonitor::EnableWebResourceResponseReceivedEvent(bool enable) { - if (!enable && m_webResourceResponseReceivedToken.value != 0) - { - m_webviewEventSourceExperimental->remove_WebResourceResponseReceived(m_webResourceResponseReceivedToken); - m_webResourceResponseReceivedToken.value = 0; - } - else if (enable && m_webResourceResponseReceivedToken.value == 0) - { - m_webviewEventSourceExperimental->add_WebResourceResponseReceived( - Callback( - [this](ICoreWebView2Experimental* webview, ICoreWebView2ExperimentalWebResourceResponseReceivedEventArgs* args) - -> HRESULT { - wil::com_ptr webResourceRequest; - CHECK_FAILURE(args->get_Request(&webResourceRequest)); - wil::com_ptr webResourceResponse; - CHECK_FAILURE(args->get_Response(&webResourceResponse)); - //! [PopulateResponseContent] - args->PopulateResponseContent( - Callback< - ICoreWebView2ExperimentalWebResourceResponseReceivedEventArgsPopulateResponseContentCompletedHandler>( - [this, webResourceRequest, webResourceResponse](HRESULT result) { - std::wstring message = - L"{ \"kind\": \"event\", \"name\": " - L"\"WebResourceResponseReceived\", \"args\": {" - L"\"request\": " + - RequestToJsonString(webResourceRequest.get()) + - L", " - L"\"response\": " + - ResponseToJsonString(webResourceResponse.get()) + L"}"; - - message += - WebViewPropertiesToJsonString(m_webviewEventSource.get()); - message += L"}"; - PostEventMessage(message); - return S_OK; - }) - .Get()); - //! [PopulateResponseContent] - return S_OK; - }) - .Get(), - &m_webResourceResponseReceivedToken); - } -} - -void ScenarioWebViewEventMonitor::EnableWebResourceRequestedEvent(bool enable) -{ - if (!enable && m_webResourceRequestedToken.value != 0) - { - m_webviewEventSource->remove_WebResourceRequested(m_webResourceRequestedToken); - m_webResourceRequestedToken.value = 0; - } - else if (enable && m_webResourceRequestedToken.value == 0) - { - m_webviewEventSource->AddWebResourceRequestedFilter( - L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL); - m_webviewEventSource->add_WebResourceRequested( - Callback( - [this](ICoreWebView2* webview, ICoreWebView2WebResourceRequestedEventArgs* args) - -> HRESULT { - wil::com_ptr webResourceRequest; - CHECK_FAILURE(args->get_Request(&webResourceRequest)); - wil::com_ptr webResourceResponse; - CHECK_FAILURE(args->get_Response(&webResourceResponse)); - - std::wstring message = L"{ \"kind\": \"event\", \"name\": " - L"\"WebResourceRequested\", \"args\": {" - L"\"request\": " + RequestToJsonString(webResourceRequest.get()) + L", " - L"\"response\": null" - L"}"; - - message += WebViewPropertiesToJsonString(m_webviewEventSource.get()); - message += L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_webResourceRequestedToken); - } -} - -void ScenarioWebViewEventMonitor::InitializeEventView(ICoreWebView2* webviewEventView) -{ - m_webviewEventView = webviewEventView; - - m_webviewEventView->add_WebMessageReceived( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) - -> HRESULT { - wil::unique_cotaskmem_string source; - CHECK_FAILURE(args->get_Source(&source)); - wil::unique_cotaskmem_string webMessageAsString; - if (SUCCEEDED(args->TryGetWebMessageAsString(&webMessageAsString))) - { - if (wcscmp(source.get(), m_sampleUri.c_str()) == 0) - { - if (wcscmp(webMessageAsString.get(), L"webResourceRequested,on") == 0) - { - EnableWebResourceRequestedEvent(true); - } - else if (wcscmp(webMessageAsString.get(), L"webResourceRequested,off") == 0) - { - EnableWebResourceRequestedEvent(false); - } - else if (wcscmp(webMessageAsString.get(), L"webResourceResponseReceived,on") == 0) - { - EnableWebResourceResponseReceivedEvent(true); - } - else if ( - wcscmp(webMessageAsString.get(), L"webResourceResponseReceived,off") == 0) - { - EnableWebResourceResponseReceivedEvent(false); - } - } - } - - return S_OK; - }) - .Get(), - &m_eventViewWebMessageReceivedToken); - - m_webviewEventSource->add_WebMessageReceived( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) - -> HRESULT { - wil::unique_cotaskmem_string source; - CHECK_FAILURE(args->get_Source(&source)); - wil::unique_cotaskmem_string webMessageAsString; - HRESULT webMessageAsStringHR = - args->TryGetWebMessageAsString(&webMessageAsString); - wil::unique_cotaskmem_string webMessageAsJson; - CHECK_FAILURE(args->get_WebMessageAsJson(&webMessageAsJson)); - - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"WebMessageReceived\", \"args\": {" - L"\"source\": " + EncodeQuote(source.get()) + L", "; - - if (SUCCEEDED(webMessageAsStringHR)) - { - message += L"\"webMessageAsString\": " + EncodeQuote(webMessageAsString.get()) + L", "; - } - else - { - message += L"\"webMessageAsString\": null, "; - } - - message += L"\"webMessageAsJson\": " + EncodeQuote(webMessageAsJson.get()) + L" " - L"}"; - message += WebViewPropertiesToJsonString(m_webviewEventSource.get()); - - message += L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_webMessageReceivedToken); - - m_webviewEventSource->add_NewWindowRequested( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2NewWindowRequestedEventArgs* args) - -> HRESULT { - BOOL handled = FALSE; - CHECK_FAILURE(args->get_Handled(&handled)); - BOOL isUserInitiated = FALSE; - CHECK_FAILURE(args->get_IsUserInitiated(&isUserInitiated)); - wil::unique_cotaskmem_string uri; - CHECK_FAILURE(args->get_Uri(&uri)); - - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"NewWindowRequested\", \"args\": {" - L"\"handled\": " + BoolToString(handled) + L", " - L"\"isUserInitiated\": " + BoolToString(isUserInitiated) + L", " - L"\"uri\": " + EncodeQuote(uri.get()) + L", " - L"\"newWindow\": null" - L"}" - + WebViewPropertiesToJsonString(m_webviewEventSource.get()) - + L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_newWindowRequestedToken); - - m_webviewEventSource->add_NavigationStarting( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) - -> HRESULT { - BOOL cancel = FALSE; - CHECK_FAILURE(args->get_Cancel(&cancel)); - BOOL isRedirected = FALSE; - CHECK_FAILURE(args->get_IsRedirected(&isRedirected)); - BOOL isUserInitiated = FALSE; - CHECK_FAILURE(args->get_IsUserInitiated(&isUserInitiated)); - wil::com_ptr requestHeaders; - CHECK_FAILURE(args->get_RequestHeaders(&requestHeaders)); - wil::unique_cotaskmem_string uri; - CHECK_FAILURE(args->get_Uri(&uri)); - UINT64 navigationId = 0; - CHECK_FAILURE(args->get_NavigationId(&navigationId)); - - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"NavigationStarting\", \"args\": {"; - - message += L"\"navigationId\": " + std::to_wstring(navigationId) + L", "; - - message += L"\"cancel\": " + BoolToString(cancel) + L", " + - L"\"isRedirected\": " + BoolToString(isRedirected) + L", " + - L"\"isUserInitiated\": " + BoolToString(isUserInitiated) + L", " + - L"\"requestHeaders\": " + RequestHeadersToJsonString(requestHeaders.get()) + L", " + - L"\"uri\": " + EncodeQuote(uri.get()) + L" " + - L"}" + - WebViewPropertiesToJsonString(m_webviewEventSource.get()) + - L"}"; - - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_navigationStartingToken); - - m_webviewEventSource->add_FrameNavigationStarting( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) - -> HRESULT { - BOOL cancel = FALSE; - CHECK_FAILURE(args->get_Cancel(&cancel)); - BOOL isRedirected = FALSE; - CHECK_FAILURE(args->get_IsRedirected(&isRedirected)); - BOOL isUserInitiated = FALSE; - CHECK_FAILURE(args->get_IsUserInitiated(&isUserInitiated)); - wil::com_ptr requestHeaders; - CHECK_FAILURE(args->get_RequestHeaders(&requestHeaders)); - wil::unique_cotaskmem_string uri; - CHECK_FAILURE(args->get_Uri(&uri)); - - std::wstring message = - L"{ \"kind\": \"event\", \"name\": " - L"\"FrameNavigationStarting\", \"args\": {" - L"\"cancel\": " + BoolToString(cancel) + L", " - L"\"isRedirected\": " + BoolToString(isRedirected) + L", " - L"\"isUserInitiated\": " + BoolToString(isUserInitiated) + L", " - L"\"requestHeaders\": " + RequestHeadersToJsonString(requestHeaders.get()) + L", " - L"\"uri\": " + EncodeQuote(uri.get()) + L" " - L"}" + - WebViewPropertiesToJsonString(m_webviewEventSource.get()) + - L"}"; - - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_frameNavigationStartingToken); - - m_webviewEventSource->add_SourceChanged( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2SourceChangedEventArgs* args) - -> HRESULT { - BOOL isNewDocument = FALSE; - CHECK_FAILURE(args->get_IsNewDocument(&isNewDocument)); - - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"SourceChanged\", \"args\": {"; - message += L"\"isNewDocument\": " + BoolToString(isNewDocument) + L"}" + - WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_navigationStartingToken); - - m_webviewEventSource->add_ContentLoading( - Callback( - [this]( - ICoreWebView2* sender, - ICoreWebView2ContentLoadingEventArgs* args) -> HRESULT { - BOOL isErrorPage = FALSE; - CHECK_FAILURE(args->get_IsErrorPage(&isErrorPage)); - UINT64 navigationId = 0; - CHECK_FAILURE(args->get_NavigationId(&navigationId)); - - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"ContentLoading\", \"args\": {"; - - message += L"\"navigationId\": " + std::to_wstring(navigationId) + L", "; - - message += L"\"isErrorPage\": " + BoolToString(isErrorPage) + L"}" + - WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_navigationStartingToken); - - m_webviewEventSource->add_HistoryChanged( - Callback( - [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT { - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"HistoryChanged\", \"args\": {"; - message += - L"}" + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_navigationStartingToken); - - m_webviewEventSource->add_NavigationCompleted( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) - -> HRESULT { - BOOL isSuccess = FALSE; - CHECK_FAILURE(args->get_IsSuccess(&isSuccess)); - COREWEBVIEW2_WEB_ERROR_STATUS webErrorStatus; - CHECK_FAILURE(args->get_WebErrorStatus(&webErrorStatus)); - UINT64 navigationId = 0; - CHECK_FAILURE(args->get_NavigationId(&navigationId)); - - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"NavigationCompleted\", \"args\": {"; - - message += L"\"navigationId\": " + std::to_wstring(navigationId) + L", "; - - message += - L"\"isSuccess\": " + BoolToString(isSuccess) + L", " - L"\"webErrorStatus\": " + EncodeQuote(WebErrorStatusToString(webErrorStatus)) + L" " - L"}" + - WebViewPropertiesToJsonString(m_webviewEventSource.get()) + - L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_navigationStartingToken); - - m_webviewEventSource->add_DocumentTitleChanged( - Callback( - [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT { - std::wstring message = - L"{ \"kind\": \"event\", \"name\": \"DocumentTitleChanged\", \"args\": {" - L"}" + - WebViewPropertiesToJsonString(m_webviewEventSource.get()) + - L"}"; - PostEventMessage(message); - - return S_OK; - }) - .Get(), - &m_navigationStartingToken); -} - -void ScenarioWebViewEventMonitor::PostEventMessage(std::wstring message) -{ - HRESULT hr = m_webviewEventView->PostWebMessageAsJson(message.c_str()); - if (FAILED(hr)) - { - ShowFailure(hr, L"PostWebMessageAsJson failed:\n" + message); - } -} +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1 + +#include "AppWindow.h" +#include "CheckFailure.h" +#include "ScenarioWebViewEventMonitor.h" +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace std; + +static constexpr wchar_t c_samplePath[] = L"ScenarioWebViewEventMonitor.html"; + +ScenarioWebViewEventMonitor::ScenarioWebViewEventMonitor(AppWindow* appWindowEventSource) + : m_appWindowEventSource(appWindowEventSource), + m_webviewEventSource(appWindowEventSource->GetWebView()) +{ + m_sampleUri = m_appWindowEventSource->GetLocalUri(c_samplePath); + m_appWindowEventView = new AppWindow( + IDM_CREATION_MODE_WINDOWED, + m_sampleUri, + false, + [this]() -> void { + InitializeEventView(m_appWindowEventView->GetWebView()); + }); + m_webviewEventSourceExperimental = m_webviewEventSource.query(); +} + +ScenarioWebViewEventMonitor::~ScenarioWebViewEventMonitor() +{ + m_webviewEventSource->remove_NavigationStarting(m_navigationStartingToken); + m_webviewEventSource->remove_FrameNavigationStarting(m_frameNavigationStartingToken); + m_webviewEventSource->remove_SourceChanged(m_sourceChangedToken); + m_webviewEventSource->remove_ContentLoading(m_contentLoadingToken); + m_webviewEventSource->remove_HistoryChanged(m_historyChangedToken); + m_webviewEventSource->remove_NavigationCompleted(m_navigationCompletedToken); + m_webviewEventSource->remove_DocumentTitleChanged(m_documentTitleChangedToken); + m_webviewEventSource->remove_WebMessageReceived(m_webMessageReceivedToken); + m_webviewEventSource->remove_NewWindowRequested(m_newWindowRequestedToken); + m_webviewEventSourceExperimental->remove_DOMContentLoaded(m_DOMContentLoadedToken); + EnableWebResourceRequestedEvent(false); + EnableWebResourceResponseReceivedEvent(false); + + m_webviewEventView->remove_WebMessageReceived(m_eventViewWebMessageReceivedToken); +} + +std::wstring WebErrorStatusToString(COREWEBVIEW2_WEB_ERROR_STATUS status) +{ + switch (status) + { +#define STATUS_ENTRY(statusValue) \ + case statusValue: \ + return L#statusValue; + + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_UNKNOWN); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_COMMON_NAME_IS_INCORRECT); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_EXPIRED); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CLIENT_CERTIFICATE_CONTAINS_ERRORS); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_REVOKED); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_SERVER_UNREACHABLE); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_TIMEOUT); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_ERROR_HTTP_INVALID_SERVER_RESPONSE); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_ABORTED); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CONNECTION_RESET); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_CANNOT_CONNECT); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_HOST_NAME_NOT_RESOLVED); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_OPERATION_CANCELED); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_REDIRECT_FAILED); + STATUS_ENTRY(COREWEBVIEW2_WEB_ERROR_STATUS_UNEXPECTED_ERROR); + +#undef STATUS_ENTRY + } + + return L"ERROR"; +} + +std::wstring BoolToString(BOOL value) +{ + return value ? L"true" : L"false"; +} + +std::wstring EncodeQuote(std::wstring raw) +{ + std::wstring encoded; + // Allocate 10 more chars to reduce memory re-allocation + // due to adding potential escaping chars. + encoded.reserve(raw.length() + 10); + encoded.push_back(L'"'); + for (int i = 0; i < raw.length(); ++i) + { + // Escape chars as listed in https://tc39.es/ecma262/#sec-json.stringify. + switch (raw[i]) + { + case '\b': + encoded.append(L"\\b"); + break; + case '\f': + encoded.append(L"\\f"); + break; + case '\n': + encoded.append(L"\\n"); + break; + case '\r': + encoded.append(L"\\r"); + break; + case '\t': + encoded.append(L"\\t"); + break; + case '\\': + encoded.append(L"\\\\"); + break; + case '"': + encoded.append(L"\\\""); + break; + default: + encoded.push_back(raw[i]); + } + } + encoded.push_back(L'"'); + return encoded; +} + +//! [HttpRequestHeaderIterator] +std::wstring RequestHeadersToJsonString(ICoreWebView2HttpRequestHeaders* requestHeaders) +{ + wil::com_ptr iterator; + CHECK_FAILURE(requestHeaders->GetIterator(&iterator)); + BOOL hasCurrent = FALSE; + std::wstring result = L"["; + + while (SUCCEEDED(iterator->get_HasCurrentHeader(&hasCurrent)) && hasCurrent) + { + wil::unique_cotaskmem_string name; + wil::unique_cotaskmem_string value; + + CHECK_FAILURE(iterator->GetCurrentHeader(&name, &value)); + result += L"{\"name\": " + EncodeQuote(name.get()) + + L", \"value\": " + EncodeQuote(value.get()) + L"}"; + + BOOL hasNext = FALSE; + CHECK_FAILURE(iterator->MoveNext(&hasNext)); + if (hasNext) + { + result += L", "; + } + } + + return result + L"]"; +} +//! [HttpRequestHeaderIterator] + +std::wstring ResponseHeadersToJsonString(ICoreWebView2HttpResponseHeaders* responseHeaders) +{ + wil::com_ptr iterator; + CHECK_FAILURE(responseHeaders->GetIterator(&iterator)); + BOOL hasCurrent = FALSE; + std::wstring result = L"["; + + while (SUCCEEDED(iterator->get_HasCurrentHeader(&hasCurrent)) && hasCurrent) + { + wil::unique_cotaskmem_string name; + wil::unique_cotaskmem_string value; + + CHECK_FAILURE(iterator->GetCurrentHeader(&name, &value)); + result += EncodeQuote(std::wstring(name.get()) + L": " + value.get()); + + BOOL hasNext = FALSE; + CHECK_FAILURE(iterator->MoveNext(&hasNext)); + if (hasNext) + { + result += L", "; + } + } + + return result + L"]"; +} + +std::wstring RequestToJsonString(ICoreWebView2WebResourceRequest* request) +{ + wil::com_ptr content; + CHECK_FAILURE(request->get_Content(&content)); + wil::com_ptr headers; + CHECK_FAILURE(request->get_Headers(&headers)); + wil::unique_cotaskmem_string method; + CHECK_FAILURE(request->get_Method(&method)); + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(request->get_Uri(&uri)); + + std::wstring result = L"{"; + + result += L"\"content\": "; + result += (content == nullptr ? L"null" : L"\"...\""); + result += L", "; + + result += L"\"headers\": " + RequestHeadersToJsonString(headers.get()) + L", "; + result += L"\"method\": " + EncodeQuote(method.get()) + L", "; + result += L"\"uri\": " + EncodeQuote(uri.get()) + L" "; + + result += L"}"; + + return result; +} + +std::wstring GetPreviewOfContent(IStream* content, bool& readAll) +{ + char buffer[50]; + unsigned long read; + content->Read(buffer, 50U, &read); + readAll = read < 50; + + WCHAR converted[50]; + CHECK_FAILURE(MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, buffer, 50, converted, 50)); + return std::wstring(converted); +} + +std::wstring ResponseToJsonString( + ICoreWebView2ExperimentalWebResourceResponseView* response, IStream* content) +{ + wil::com_ptr headers; + CHECK_FAILURE(response->get_Headers(&headers)); + int statusCode; + CHECK_FAILURE(response->get_StatusCode(&statusCode)); + wil::unique_cotaskmem_string reasonPhrase; + CHECK_FAILURE(response->get_ReasonPhrase(&reasonPhrase)); + BOOL containsContentType = FALSE; + headers->Contains(L"Content-Type", &containsContentType); + wil::unique_cotaskmem_string contentType; + bool isBinaryContent = true; + if (containsContentType) + { + headers->GetHeader(L"Content-Type", &contentType); + if (wcsncmp(L"text/", contentType.get(), ARRAYSIZE(L"text/")) == 0) + { + isBinaryContent = false; + } + } + std::wstring result = L"{"; + + result += L"\"content\": "; + if (!content) + { + result += L"null"; + } + else + { + if (isBinaryContent) + { + result += EncodeQuote(L"BINARY_DATA"); + } + else + { + bool readAll = false; + result += EncodeQuote(GetPreviewOfContent(content, readAll)); + if (!readAll) + { + result += L"..."; + } + } + } + result += L", "; + + result += L"\"headers\": " + ResponseHeadersToJsonString(headers.get()) + L", "; + result += L"\"status\": "; + WCHAR statusCodeString[4]; + _itow_s(statusCode, statusCodeString, 4, 10); + result += statusCodeString; + result += L", "; + result += L"\"reason\": " + EncodeQuote(reasonPhrase.get()) + L" "; + + result += L"}"; + + return result; +} + +std::wstring WebViewPropertiesToJsonString(ICoreWebView2* webview) +{ + wil::unique_cotaskmem_string documentTitle; + CHECK_FAILURE(webview->get_DocumentTitle(&documentTitle)); + wil::unique_cotaskmem_string source; + CHECK_FAILURE(webview->get_Source(&source)); + + std::wstring result = L", \"webview\": {" + L"\"documentTitle\": " + EncodeQuote(documentTitle.get()) + L", " + + L"\"source\": " + EncodeQuote(source.get()) + L" " + + L"}"; + + return result; +} + +void ScenarioWebViewEventMonitor::EnableWebResourceResponseReceivedEvent(bool enable) { + if (!enable && m_webResourceResponseReceivedToken.value != 0) + { + m_webviewEventSourceExperimental->remove_WebResourceResponseReceived(m_webResourceResponseReceivedToken); + m_webResourceResponseReceivedToken.value = 0; + } + else if (enable && m_webResourceResponseReceivedToken.value == 0) + { + m_webviewEventSourceExperimental->add_WebResourceResponseReceived( + Callback( + [this](ICoreWebView2Experimental* webview, ICoreWebView2ExperimentalWebResourceResponseReceivedEventArgs* args) + -> HRESULT { + wil::com_ptr webResourceRequest; + CHECK_FAILURE(args->get_Request(&webResourceRequest)); + wil::com_ptr + webResourceResponse; + CHECK_FAILURE(args->get_Response(&webResourceResponse)); + //! [GetContent] + webResourceResponse->GetContent( + Callback< + ICoreWebView2ExperimentalWebResourceResponseViewGetContentCompletedHandler>( + [this, webResourceRequest, + webResourceResponse](HRESULT result, IStream* content) { + std::wstring message = + L"{ \"kind\": \"event\", \"name\": " + L"\"WebResourceResponseReceived\", \"args\": {" + L"\"request\": " + + RequestToJsonString(webResourceRequest.get()) + + L", " + L"\"response\": " + + ResponseToJsonString(webResourceResponse.get(), content) + + L"}"; + + message += + WebViewPropertiesToJsonString(m_webviewEventSource.get()); + message += L"}"; + PostEventMessage(message); + return S_OK; + }) + .Get()); + //! [GetContent] + return S_OK; + }) + .Get(), + &m_webResourceResponseReceivedToken); + } +} + +void ScenarioWebViewEventMonitor::EnableWebResourceRequestedEvent(bool enable) +{ + if (!enable && m_webResourceRequestedToken.value != 0) + { + m_webviewEventSource->remove_WebResourceRequested(m_webResourceRequestedToken); + m_webResourceRequestedToken.value = 0; + } + else if (enable && m_webResourceRequestedToken.value == 0) + { + m_webviewEventSource->AddWebResourceRequestedFilter( + L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL); + m_webviewEventSource->add_WebResourceRequested( + Callback( + [this](ICoreWebView2* webview, ICoreWebView2WebResourceRequestedEventArgs* args) + -> HRESULT { + wil::com_ptr webResourceRequest; + CHECK_FAILURE(args->get_Request(&webResourceRequest)); + wil::com_ptr webResourceResponse; + CHECK_FAILURE(args->get_Response(&webResourceResponse)); + + std::wstring message = L"{ \"kind\": \"event\", \"name\": " + L"\"WebResourceRequested\", \"args\": {" + L"\"request\": " + RequestToJsonString(webResourceRequest.get()) + L", " + L"\"response\": null" + L"}"; + + message += WebViewPropertiesToJsonString(m_webviewEventSource.get()); + message += L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_webResourceRequestedToken); + } +} + +void ScenarioWebViewEventMonitor::InitializeEventView(ICoreWebView2* webviewEventView) +{ + m_webviewEventView = webviewEventView; + + m_webviewEventView->add_WebMessageReceived( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) + -> HRESULT { + wil::unique_cotaskmem_string source; + CHECK_FAILURE(args->get_Source(&source)); + wil::unique_cotaskmem_string webMessageAsString; + if (SUCCEEDED(args->TryGetWebMessageAsString(&webMessageAsString))) + { + if (wcscmp(source.get(), m_sampleUri.c_str()) == 0) + { + if (wcscmp(webMessageAsString.get(), L"webResourceRequested,on") == 0) + { + EnableWebResourceRequestedEvent(true); + } + else if (wcscmp(webMessageAsString.get(), L"webResourceRequested,off") == 0) + { + EnableWebResourceRequestedEvent(false); + } + else if (wcscmp(webMessageAsString.get(), L"webResourceResponseReceived,on") == 0) + { + EnableWebResourceResponseReceivedEvent(true); + } + else if ( + wcscmp(webMessageAsString.get(), L"webResourceResponseReceived,off") == 0) + { + EnableWebResourceResponseReceivedEvent(false); + } + } + } + + return S_OK; + }) + .Get(), + &m_eventViewWebMessageReceivedToken); + + m_webviewEventSource->add_WebMessageReceived( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args) + -> HRESULT { + wil::unique_cotaskmem_string source; + CHECK_FAILURE(args->get_Source(&source)); + wil::unique_cotaskmem_string webMessageAsString; + HRESULT webMessageAsStringHR = + args->TryGetWebMessageAsString(&webMessageAsString); + wil::unique_cotaskmem_string webMessageAsJson; + CHECK_FAILURE(args->get_WebMessageAsJson(&webMessageAsJson)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"WebMessageReceived\", \"args\": {" + L"\"source\": " + EncodeQuote(source.get()) + L", "; + + if (SUCCEEDED(webMessageAsStringHR)) + { + message += L"\"webMessageAsString\": " + EncodeQuote(webMessageAsString.get()) + L", "; + } + else + { + message += L"\"webMessageAsString\": null, "; + } + + message += L"\"webMessageAsJson\": " + EncodeQuote(webMessageAsJson.get()) + L" " + L"}"; + message += WebViewPropertiesToJsonString(m_webviewEventSource.get()); + + message += L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_webMessageReceivedToken); + + m_webviewEventSource->add_NewWindowRequested( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NewWindowRequestedEventArgs* args) + -> HRESULT { + BOOL handled = FALSE; + CHECK_FAILURE(args->get_Handled(&handled)); + BOOL isUserInitiated = FALSE; + CHECK_FAILURE(args->get_IsUserInitiated(&isUserInitiated)); + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(args->get_Uri(&uri)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"NewWindowRequested\", \"args\": {" + L"\"handled\": " + BoolToString(handled) + L", " + L"\"isUserInitiated\": " + BoolToString(isUserInitiated) + L", " + L"\"uri\": " + EncodeQuote(uri.get()) + L", " + L"\"newWindow\": null" + L"}" + + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + + L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_newWindowRequestedToken); + + m_webviewEventSource->add_NavigationStarting( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) + -> HRESULT { + BOOL cancel = FALSE; + CHECK_FAILURE(args->get_Cancel(&cancel)); + BOOL isRedirected = FALSE; + CHECK_FAILURE(args->get_IsRedirected(&isRedirected)); + BOOL isUserInitiated = FALSE; + CHECK_FAILURE(args->get_IsUserInitiated(&isUserInitiated)); + wil::com_ptr requestHeaders; + CHECK_FAILURE(args->get_RequestHeaders(&requestHeaders)); + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(args->get_Uri(&uri)); + UINT64 navigationId = 0; + CHECK_FAILURE(args->get_NavigationId(&navigationId)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"NavigationStarting\", \"args\": {"; + + message += L"\"navigationId\": " + std::to_wstring(navigationId) + L", "; + + message += L"\"cancel\": " + BoolToString(cancel) + L", " + + L"\"isRedirected\": " + BoolToString(isRedirected) + L", " + + L"\"isUserInitiated\": " + BoolToString(isUserInitiated) + L", " + + L"\"requestHeaders\": " + RequestHeadersToJsonString(requestHeaders.get()) + L", " + + L"\"uri\": " + EncodeQuote(uri.get()) + L" " + + L"}" + + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + + L"}"; + + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_navigationStartingToken); + + m_webviewEventSource->add_FrameNavigationStarting( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) + -> HRESULT { + BOOL cancel = FALSE; + CHECK_FAILURE(args->get_Cancel(&cancel)); + BOOL isRedirected = FALSE; + CHECK_FAILURE(args->get_IsRedirected(&isRedirected)); + BOOL isUserInitiated = FALSE; + CHECK_FAILURE(args->get_IsUserInitiated(&isUserInitiated)); + wil::com_ptr requestHeaders; + CHECK_FAILURE(args->get_RequestHeaders(&requestHeaders)); + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(args->get_Uri(&uri)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": " + L"\"FrameNavigationStarting\", \"args\": {" + L"\"cancel\": " + BoolToString(cancel) + L", " + L"\"isRedirected\": " + BoolToString(isRedirected) + L", " + L"\"isUserInitiated\": " + BoolToString(isUserInitiated) + L", " + L"\"requestHeaders\": " + RequestHeadersToJsonString(requestHeaders.get()) + L", " + L"\"uri\": " + EncodeQuote(uri.get()) + L" " + L"}" + + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + + L"}"; + + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_frameNavigationStartingToken); + + m_webviewEventSource->add_SourceChanged( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2SourceChangedEventArgs* args) + -> HRESULT { + BOOL isNewDocument = FALSE; + CHECK_FAILURE(args->get_IsNewDocument(&isNewDocument)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"SourceChanged\", \"args\": {"; + message += L"\"isNewDocument\": " + BoolToString(isNewDocument) + L"}" + + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_sourceChangedToken); + + m_webviewEventSource->add_ContentLoading( + Callback( + [this]( + ICoreWebView2* sender, + ICoreWebView2ContentLoadingEventArgs* args) -> HRESULT { + BOOL isErrorPage = FALSE; + CHECK_FAILURE(args->get_IsErrorPage(&isErrorPage)); + UINT64 navigationId = 0; + CHECK_FAILURE(args->get_NavigationId(&navigationId)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"ContentLoading\", \"args\": {"; + + message += L"\"navigationId\": " + std::to_wstring(navigationId) + L", "; + + message += L"\"isErrorPage\": " + BoolToString(isErrorPage) + L"}" + + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_contentLoadingToken); + + m_webviewEventSource->add_HistoryChanged( + Callback( + [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT { + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"HistoryChanged\", \"args\": {"; + message += + L"}" + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_historyChangedToken); + + m_webviewEventSource->add_NavigationCompleted( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationCompletedEventArgs* args) + -> HRESULT { + BOOL isSuccess = FALSE; + CHECK_FAILURE(args->get_IsSuccess(&isSuccess)); + COREWEBVIEW2_WEB_ERROR_STATUS webErrorStatus; + CHECK_FAILURE(args->get_WebErrorStatus(&webErrorStatus)); + UINT64 navigationId = 0; + CHECK_FAILURE(args->get_NavigationId(&navigationId)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"NavigationCompleted\", \"args\": {"; + + message += L"\"navigationId\": " + std::to_wstring(navigationId) + L", "; + + message += + L"\"isSuccess\": " + BoolToString(isSuccess) + L", " + L"\"webErrorStatus\": " + EncodeQuote(WebErrorStatusToString(webErrorStatus)) + L" " + L"}" + + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + + L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_navigationCompletedToken); + + m_webviewEventSourceExperimental->add_DOMContentLoaded( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2ExperimentalDOMContentLoadedEventArgs* args) + -> HRESULT { + UINT64 navigationId = 0; + CHECK_FAILURE(args->get_NavigationId(&navigationId)); + + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"DOMContentLoaded\", \"args\": {"; + + message += L"\"navigationId\": " + std::to_wstring(navigationId) + L", "; + + message += + L"}" + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_DOMContentLoadedToken); + + m_webviewEventSource->add_DocumentTitleChanged( + Callback( + [this](ICoreWebView2* sender, IUnknown* args) -> HRESULT { + std::wstring message = + L"{ \"kind\": \"event\", \"name\": \"DocumentTitleChanged\", \"args\": {" + L"}" + + WebViewPropertiesToJsonString(m_webviewEventSource.get()) + + L"}"; + PostEventMessage(message); + + return S_OK; + }) + .Get(), + &m_documentTitleChangedToken); +} + +void ScenarioWebViewEventMonitor::PostEventMessage(std::wstring message) +{ + HRESULT hr = m_webviewEventView->PostWebMessageAsJson(message.c_str()); + if (FAILED(hr)) + { + ShowFailure(hr, L"PostWebMessageAsJson failed:\n" + message); + } +} diff --git a/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.h b/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.h index 17ec8d91..142665a6 100644 --- a/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.h +++ b/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.h @@ -1,61 +1,62 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "stdafx.h" -#include -#include "ComponentBase.h" - -std::wstring WebErrorStatusToString(COREWEBVIEW2_WEB_ERROR_STATUS status); - -// The event monitor examines events from the m_appWindowEventSource and -// m_webviewEventSource and displays the details of those events in -// m_appWindowEventView and m_webviewEventView. -class ScenarioWebViewEventMonitor : public ComponentBase -{ -public: - ScenarioWebViewEventMonitor(AppWindow* appWindowEventSource); - ~ScenarioWebViewEventMonitor() override; - - void InitializeEventView(ICoreWebView2* webviewEventView); - -private: - // Because WebResourceRequested fires so much more often than - // all other events, we default to it off and it is configurable. - void EnableWebResourceRequestedEvent(bool enable); - - void EnableWebResourceResponseReceivedEvent(bool enable); - // Send information about an event to the event view. - void PostEventMessage(std::wstring messageAsJson); - - // The event view displays the events and their details. - AppWindow* m_appWindowEventView; - wil::com_ptr m_webviewEventView; - // The URI of the HTML document that displays the events. - std::wstring m_sampleUri; - - // The event source objects fire the events. - AppWindow* m_appWindowEventSource; - wil::com_ptr m_webviewEventSource; - wil::com_ptr m_webviewEventSourceExperimental; - - // The events we register on the event source - EventRegistrationToken m_frameNavigationStartingToken = {}; - EventRegistrationToken m_navigationStartingToken = {}; - EventRegistrationToken m_sourceChangedToken = {}; - EventRegistrationToken m_contentLoadingToken = {}; - EventRegistrationToken m_historyChangedToken = {}; - EventRegistrationToken m_navigationCompletedToken = {}; - EventRegistrationToken m_documentTitleChangedToken = {}; - EventRegistrationToken m_webMessageReceivedToken = {}; - EventRegistrationToken m_webResourceRequestedToken = {}; - EventRegistrationToken m_newWindowRequestedToken = {}; - EventRegistrationToken m_webResourceResponseReceivedToken = {}; - - // This event is registered with the event viewer so they - // can communicate back to us for toggling the WebResourceRequested - // event. - EventRegistrationToken m_eventViewWebMessageReceivedToken = {}; -}; +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "stdafx.h" +#include +#include "ComponentBase.h" + +std::wstring WebErrorStatusToString(COREWEBVIEW2_WEB_ERROR_STATUS status); + +// The event monitor examines events from the m_appWindowEventSource and +// m_webviewEventSource and displays the details of those events in +// m_appWindowEventView and m_webviewEventView. +class ScenarioWebViewEventMonitor : public ComponentBase +{ +public: + ScenarioWebViewEventMonitor(AppWindow* appWindowEventSource); + ~ScenarioWebViewEventMonitor() override; + + void InitializeEventView(ICoreWebView2* webviewEventView); + +private: + // Because WebResourceRequested fires so much more often than + // all other events, we default to it off and it is configurable. + void EnableWebResourceRequestedEvent(bool enable); + + void EnableWebResourceResponseReceivedEvent(bool enable); + // Send information about an event to the event view. + void PostEventMessage(std::wstring messageAsJson); + + // The event view displays the events and their details. + AppWindow* m_appWindowEventView; + wil::com_ptr m_webviewEventView; + // The URI of the HTML document that displays the events. + std::wstring m_sampleUri; + + // The event source objects fire the events. + AppWindow* m_appWindowEventSource; + wil::com_ptr m_webviewEventSource; + wil::com_ptr m_webviewEventSourceExperimental; + + // The events we register on the event source + EventRegistrationToken m_frameNavigationStartingToken = {}; + EventRegistrationToken m_navigationStartingToken = {}; + EventRegistrationToken m_sourceChangedToken = {}; + EventRegistrationToken m_contentLoadingToken = {}; + EventRegistrationToken m_historyChangedToken = {}; + EventRegistrationToken m_navigationCompletedToken = {}; + EventRegistrationToken m_DOMContentLoadedToken = {}; + EventRegistrationToken m_documentTitleChangedToken = {}; + EventRegistrationToken m_webMessageReceivedToken = {}; + EventRegistrationToken m_webResourceRequestedToken = {}; + EventRegistrationToken m_newWindowRequestedToken = {}; + EventRegistrationToken m_webResourceResponseReceivedToken = {}; + + // This event is registered with the event viewer so they + // can communicate back to us for toggling the WebResourceRequested + // event. + EventRegistrationToken m_eventViewWebMessageReceivedToken = {}; +}; diff --git a/SampleApps/WebView2APISample/SettingsComponent.cpp b/SampleApps/WebView2APISample/SettingsComponent.cpp index bc7f7e7a..91b2dfc0 100644 --- a/SampleApps/WebView2APISample/SettingsComponent.cpp +++ b/SampleApps/WebView2APISample/SettingsComponent.cpp @@ -1,656 +1,660 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "stdafx.h" - -#include "SettingsComponent.h" - -#include "CheckFailure.h" -#include "TextInputDialog.h" - -using namespace Microsoft::WRL; - -// Some utility functions -static wil::unique_bstr GetDomainOfUri(PWSTR uri); -static PCWSTR NameOfPermissionKind(COREWEBVIEW2_PERMISSION_KIND kind); - -SettingsComponent::SettingsComponent( - AppWindow* appWindow, ICoreWebView2Environment* environment, SettingsComponent* old) - : m_appWindow(appWindow), m_webViewEnvironment(environment), - m_webView(appWindow->GetWebView()) -{ - CHECK_FAILURE(m_webView->get_Settings(&m_settings)); - - // Copy old settings if desired - if (old) - { - BOOL setting; - CHECK_FAILURE(old->m_settings->get_IsScriptEnabled(&setting)); - CHECK_FAILURE(m_settings->put_IsScriptEnabled(setting)); - CHECK_FAILURE(old->m_settings->get_IsWebMessageEnabled(&setting)); - CHECK_FAILURE(m_settings->put_IsWebMessageEnabled(setting)); - CHECK_FAILURE(old->m_settings->get_AreDefaultScriptDialogsEnabled(&setting)); - CHECK_FAILURE(m_settings->put_AreDefaultScriptDialogsEnabled(setting)); - CHECK_FAILURE(old->m_settings->get_IsStatusBarEnabled(&setting)); - CHECK_FAILURE(m_settings->put_IsStatusBarEnabled(setting)); - CHECK_FAILURE(old->m_settings->get_AreDevToolsEnabled(&setting)); - CHECK_FAILURE(m_settings->put_AreDevToolsEnabled(setting)); - SetBlockImages(old->m_blockImages); - SetUserAgent(old->m_overridingUserAgent); - m_deferScriptDialogs = old->m_deferScriptDialogs; - m_isScriptEnabled = old->m_isScriptEnabled; - m_blockedSitesSet = old->m_blockedSitesSet; - m_blockedSites = std::move(old->m_blockedSites); - m_overridingUserAgent = std::move(old->m_overridingUserAgent); - } - - //! [NavigationStarting] - // Register a handler for the NavigationStarting event. - // This handler will check the domain being navigated to, and if the domain - // matches a list of blocked sites, it will cancel the navigation and - // possibly display a warning page. It will also disable JavaScript on - // selected websites. - CHECK_FAILURE(m_webView->add_NavigationStarting( - Callback( - [this](ICoreWebView2* sender, - ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT - { - wil::unique_cotaskmem_string uri; - CHECK_FAILURE(args->get_Uri(&uri)); - - if (ShouldBlockUri(uri.get())) - { - CHECK_FAILURE(args->put_Cancel(true)); - - // If the user clicked a link to navigate, show a warning page. - BOOL userInitiated; - CHECK_FAILURE(args->get_IsUserInitiated(&userInitiated)); - //! [NavigateToString] - static const PCWSTR htmlContent = - L"

Domain Blocked

" - L"

You've attempted to navigate to a domain in the blocked " - L"sites list. Press back to return to the previous page.

"; - CHECK_FAILURE(sender->NavigateToString(htmlContent)); - //! [NavigateToString] - } - //! [IsScriptEnabled] - // Changes to settings will apply at the next navigation, which includes the - // navigation after a NavigationStarting event. We can use this to change - // settings according to what site we're visiting. - if (ShouldBlockScriptForUri(uri.get())) - { - m_settings->put_IsScriptEnabled(FALSE); - } - else - { - m_settings->put_IsScriptEnabled(m_isScriptEnabled); - } - //! [IsScriptEnabled] - return S_OK; - }).Get(), &m_navigationStartingToken)); - //! [NavigationStarting] - - //! [FrameNavigationStarting] - // Register a handler for the FrameNavigationStarting event. - // This handler will prevent a frame from navigating to a blocked domain. - CHECK_FAILURE(m_webView->add_FrameNavigationStarting( - Callback( - [this](ICoreWebView2* sender, - ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT - { - wil::unique_cotaskmem_string uri; - CHECK_FAILURE(args->get_Uri(&uri)); - - if (ShouldBlockUri(uri.get())) - { - CHECK_FAILURE(args->put_Cancel(true)); - } - return S_OK; - }).Get(), &m_frameNavigationStartingToken)); - //! [FrameNavigationStarting] - - //! [ScriptDialogOpening] - // Register a handler for the ScriptDialogOpening event. - // This handler will set up a custom prompt dialog for the user, - // and may defer the event if the setting to defer dialogs is enabled. - CHECK_FAILURE(m_webView->add_ScriptDialogOpening( - Callback( - [this]( - ICoreWebView2* sender, - ICoreWebView2ScriptDialogOpeningEventArgs* args) -> HRESULT - { - wil::com_ptr eventArgs = args; - auto showDialog = [this, eventArgs] - { - wil::unique_cotaskmem_string uri; - COREWEBVIEW2_SCRIPT_DIALOG_KIND type; - wil::unique_cotaskmem_string message; - wil::unique_cotaskmem_string defaultText; - - CHECK_FAILURE(eventArgs->get_Uri(&uri)); - CHECK_FAILURE(eventArgs->get_Kind(&type)); - CHECK_FAILURE(eventArgs->get_Message(&message)); - CHECK_FAILURE(eventArgs->get_DefaultText(&defaultText)); - - std::wstring promptString = std::wstring(L"The page at '") - + uri.get() + L"' says:"; - TextInputDialog dialog( - m_appWindow->GetMainWindow(), - L"Script Dialog", - promptString.c_str(), - message.get(), - defaultText.get(), - /* readonly */ type != COREWEBVIEW2_SCRIPT_DIALOG_KIND_PROMPT); - if (dialog.confirmed) - { - CHECK_FAILURE(eventArgs->put_ResultText(dialog.input.c_str())); - CHECK_FAILURE(eventArgs->Accept()); - } - }; - - if (m_deferScriptDialogs) - { - wil::com_ptr deferral; - CHECK_FAILURE(args->GetDeferral(&deferral)); - m_completeDeferredDialog = [showDialog, deferral] - { - showDialog(); - CHECK_FAILURE(deferral->Complete()); - }; - } - else - { - showDialog(); - } - - return S_OK; - }).Get(), &m_scriptDialogOpeningToken)); - //! [ScriptDialogOpening] - - //! [PermissionRequested] - // Register a handler for the PermissionRequested event. - // This handler prompts the user to allow or deny the request. - CHECK_FAILURE(m_webView->add_PermissionRequested( - Callback( - [this]( - ICoreWebView2* sender, - ICoreWebView2PermissionRequestedEventArgs* args) -> HRESULT - { - wil::unique_cotaskmem_string uri; - COREWEBVIEW2_PERMISSION_KIND kind = COREWEBVIEW2_PERMISSION_KIND_UNKNOWN_PERMISSION; - BOOL userInitiated = FALSE; - - CHECK_FAILURE(args->get_Uri(&uri)); - CHECK_FAILURE(args->get_PermissionKind(&kind)); - CHECK_FAILURE(args->get_IsUserInitiated(&userInitiated)); - - std::wstring message = L"Do you want to grant permission for "; - message += NameOfPermissionKind(kind); - message += L" to the website at "; - message += uri.get(); - message += L"?\n\n"; - message += (userInitiated - ? L"This request came from a user gesture." - : L"This request did not come from a user gesture."); - - int response = MessageBox(nullptr, message.c_str(), L"Permission Request", - MB_YESNOCANCEL | MB_ICONWARNING); - - COREWEBVIEW2_PERMISSION_STATE state = - response == IDYES ? COREWEBVIEW2_PERMISSION_STATE_ALLOW - : response == IDNO ? COREWEBVIEW2_PERMISSION_STATE_DENY - : COREWEBVIEW2_PERMISSION_STATE_DEFAULT; - CHECK_FAILURE(args->put_State(state)); - - return S_OK; - }).Get(), &m_permissionRequestedToken)); - //! [PermissionRequested] -} - -bool SettingsComponent::HandleWindowMessage( - HWND hWnd, - UINT message, - WPARAM wParam, - LPARAM lParam, - LRESULT* result) -{ - if (message == WM_COMMAND) - { - switch (LOWORD(wParam)) - { - case ID_BLOCKEDSITES: - { - ChangeBlockedSites(); - return true; - } - case ID_SETTINGS_SETUSERAGENT: - { - ChangeUserAgent(); - return true; - } - case IDM_TOGGLE_JAVASCRIPT: - { - m_isScriptEnabled = !m_isScriptEnabled; - m_settings->put_IsScriptEnabled(m_isScriptEnabled); - MessageBox(nullptr, - (std::wstring(L"JavaScript will be ") - + (m_isScriptEnabled ? L"enabled" : L"disabled") - + L" after the next navigation.").c_str(), - L"Settings change", MB_OK); - return true; - } - case IDM_TOGGLE_WEB_MESSAGING: - { - BOOL isWebMessageEnabled; - m_settings->get_IsWebMessageEnabled(&isWebMessageEnabled); - m_settings->put_IsWebMessageEnabled(!isWebMessageEnabled); - MessageBox(nullptr, - (std::wstring(L"Web Messaging will be ") - + (!isWebMessageEnabled ? L"enabled" : L"disabled") - + L" after the next navigation.").c_str(), - L"Settings change", MB_OK); - return true; - } - case ID_SETTINGS_STATUS_BAR_ENABLED: - { - BOOL isStatusBarEnabled; - m_settings->get_IsStatusBarEnabled(&isStatusBarEnabled); - m_settings->put_IsStatusBarEnabled(!isStatusBarEnabled); - MessageBox(nullptr, - (std::wstring(L"Status bar will be ") + - + (!isStatusBarEnabled ? L"enabled" : L"disabled") - + L" after the next navigation.").c_str(), - L"Settings change", MB_OK); - return true; - } - case ID_SETTINGS_DEV_TOOLS_ENABLED: - { - BOOL areDevToolsEnabled = FALSE; - m_settings->get_AreDevToolsEnabled(&areDevToolsEnabled); - m_settings->put_AreDevToolsEnabled(!areDevToolsEnabled); - MessageBox(nullptr, - (std::wstring(L"Dev tools will be ") - + (!areDevToolsEnabled ? L"enabled" : L"disabled") - + L" after the next navigation.").c_str(), - L"Settings change", MB_OK); - return true; - } - case IDM_USE_DEFAULT_SCRIPT_DIALOGS: - { - BOOL defaultCurrentlyEnabled; - m_settings->get_AreDefaultScriptDialogsEnabled(&defaultCurrentlyEnabled); - if (!defaultCurrentlyEnabled) - { - m_settings->put_AreDefaultScriptDialogsEnabled(TRUE); - MessageBox(nullptr, - L"Default script dialogs will be used after the next navigation.", - L"Settings change", MB_OK); - } - return true; - } - case IDM_USE_CUSTOM_SCRIPT_DIALOGS: - { - BOOL defaultCurrentlyEnabled; - m_settings->get_AreDefaultScriptDialogsEnabled(&defaultCurrentlyEnabled); - if (defaultCurrentlyEnabled) - { - m_settings->put_AreDefaultScriptDialogsEnabled(FALSE); - m_deferScriptDialogs = false; - MessageBox(nullptr, - L"Custom script dialogs without deferral will be used after the next navigation.", - L"Settings change", MB_OK); - } - else if (m_deferScriptDialogs) - { - m_deferScriptDialogs = false; - MessageBox(nullptr, - L"Custom script dialogs without deferral will be used now.", - L"Settings change", MB_OK); - } - return true; - } - case IDM_USE_DEFERRED_SCRIPT_DIALOGS: - { - BOOL defaultCurrentlyEnabled; - m_settings->get_AreDefaultScriptDialogsEnabled(&defaultCurrentlyEnabled); - if (defaultCurrentlyEnabled) - { - m_settings->put_AreDefaultScriptDialogsEnabled(FALSE); - m_deferScriptDialogs = true; - MessageBox(nullptr, - L"Custom script dialogs with deferral will be used after the next navigation.", - L"Settings change", MB_OK); - } - else if (!m_deferScriptDialogs) - { - m_deferScriptDialogs = true; - MessageBox(nullptr, - L"Custom script dialogs with deferral will be used now.", - L"Settings change", MB_OK); - } - return true; - } - case IDM_COMPLETE_JAVASCRIPT_DIALOG: - { - CompleteScriptDialogDeferral(); - return true; - } - case ID_SETTINGS_BLOCKALLIMAGES: - { - SetBlockImages(!m_blockImages); - MessageBox(nullptr, - (std::wstring(L"Image blocking has been ") + - (m_blockImages ? L"enabled." : L"disabled.")) - .c_str(), - L"Settings change", MB_OK); - return true; - } - case ID_SETTINGS_CONTEXT_MENUS_ENABLED: - { - //! [DisableContextMenu] - BOOL allowContextMenus; - CHECK_FAILURE(m_settings->get_AreDefaultContextMenusEnabled( - &allowContextMenus)); - if (allowContextMenus) { - CHECK_FAILURE(m_settings->put_AreDefaultContextMenusEnabled(FALSE)); - MessageBox(nullptr, - L"Context menus will be disabled after the next navigation.", - L"Settings change", MB_OK); - } - else { - CHECK_FAILURE(m_settings->put_AreDefaultContextMenusEnabled(TRUE)); - MessageBox(nullptr, - L"Context menus will be enabled after the next navigation.", - L"Settings change", MB_OK); - } - //! [DisableContextMenu] - return true; - } - case ID_SETTINGS_HOST_OBJECTS_ALLOWED: - { - //! [HostObjectsAccess] - BOOL allowHostObjects; - CHECK_FAILURE(m_settings->get_AreHostObjectsAllowed(&allowHostObjects)); - if (allowHostObjects) - { - CHECK_FAILURE(m_settings->put_AreHostObjectsAllowed(FALSE)); - MessageBox( - nullptr, L"Access to host objects will be denied after the next navigation.", - L"Settings change", MB_OK); - } - else - { - CHECK_FAILURE(m_settings->put_AreHostObjectsAllowed(TRUE)); - MessageBox( - nullptr, L"Access to host objects will be allowed after the next navigation.", - L"Settings change", MB_OK); - } - //! [HostObjectsAccess] - return true; - } - case ID_SETTINGS_ZOOM_ENABLED: - { - //! [DisableZoomControl] - BOOL zoomControlEnabled; - CHECK_FAILURE(m_settings->get_IsZoomControlEnabled(&zoomControlEnabled)); - if (zoomControlEnabled) - { - CHECK_FAILURE(m_settings->put_IsZoomControlEnabled(FALSE)); - MessageBox( - nullptr, L"Zoom control will be disabled after the next navigation.", L"Settings change", - MB_OK); - } - else - { - CHECK_FAILURE(m_settings->put_IsZoomControlEnabled(TRUE)); - MessageBox( - nullptr, L"Zoom control will be enabled after the next navigation.", L"Settings change", - MB_OK); - } - //! [DisableZoomControl] - return true; - } - case ID_SETTINGS_BUILTIN_ERROR_PAGE_ENABLED: - { - //! [BuiltInErrorPageEnabled] - BOOL enabled; - CHECK_FAILURE(m_settings->get_IsBuiltInErrorPageEnabled(&enabled)); - if (enabled) - { - CHECK_FAILURE(m_settings->put_IsBuiltInErrorPageEnabled(FALSE)); - MessageBox( - nullptr, L"Built-in error page will be disabled for future navigation.", - L"Settings change", MB_OK); - } - else - { - CHECK_FAILURE(m_settings->put_IsBuiltInErrorPageEnabled(TRUE)); - MessageBox( - nullptr, L"Built-in error page will be enabled for future navigation.", - L"Settings change", MB_OK); - } - //! [BuiltInErrorPageEnabled] - return true; - } - } - } - return false; -} - -// Prompt the user for a list of blocked domains -void SettingsComponent::ChangeBlockedSites() -{ - std::wstring blockedSitesString; - if (m_blockedSitesSet) - { - for (auto& site : m_blockedSites) - { - if (!blockedSitesString.empty()) - { - blockedSitesString += L";"; - } - blockedSitesString += site; - } - } - else - { - blockedSitesString = L"foo.com;bar.org"; - } - - TextInputDialog dialog( - m_appWindow->GetMainWindow(), - L"Blocked Sites", - L"Sites:", - L"Enter hostnames to block, separated by semicolons.", - blockedSitesString.c_str()); - if (dialog.confirmed) - { - m_blockedSitesSet = true; - m_blockedSites.clear(); - size_t begin = 0; - size_t end = 0; - while (end != std::wstring::npos) - { - end = dialog.input.find(L';', begin); - if (end != begin) - { - m_blockedSites.push_back(dialog.input.substr(begin, end - begin)); - } - begin = end + 1; - } - } -} - -// Check the URI's domain against the blocked sites list -bool SettingsComponent::ShouldBlockUri(PWSTR uri) -{ - wil::unique_bstr domain = GetDomainOfUri(uri); - - for (auto site = m_blockedSites.begin(); - site != m_blockedSites.end(); site++) - { - if (wcscmp(site->c_str(), domain.get()) == 0) - { - return true; - } - } - return false; -} - -// Decide whether a website should have script disabled. Since we're only using this -// for sample code and we don't actually want to break any websites, just return false. -bool SettingsComponent::ShouldBlockScriptForUri(PWSTR uri) -{ - return false; -} - -// Turn on or off image blocking by adding or removing a WebResourceRequested handler -// which selectively intercepts requests for images. -void SettingsComponent::SetBlockImages(bool blockImages) -{ - if (blockImages != m_blockImages) - { - m_blockImages = blockImages; - - //! [WebResourceRequested] - if (m_blockImages) - { - m_webView->AddWebResourceRequestedFilter(L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE); - CHECK_FAILURE(m_webView->add_WebResourceRequested( - Callback( - [this]( - ICoreWebView2* sender, - ICoreWebView2WebResourceRequestedEventArgs* args) { - COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext; - CHECK_FAILURE( - args->get_ResourceContext(&resourceContext)); - // Ensure that the type is image - if (resourceContext != COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE) - { - return E_INVALIDARG; - } - // Override the response with an empty one to block the image. - // If put_Response is not called, the request will continue as normal. - wil::com_ptr response; - CHECK_FAILURE(m_webViewEnvironment->CreateWebResourceResponse( - nullptr, 403 /*NoContent*/, L"Blocked", L"", &response)); - CHECK_FAILURE(args->put_Response(response.get())); - return S_OK; - }) - .Get(), - &m_webResourceRequestedTokenForImageBlocking)); - } - else - { - CHECK_FAILURE(m_webView->remove_WebResourceRequested( - m_webResourceRequestedTokenForImageBlocking)); - } - //! [WebResourceRequested] - } -} - -// Prompt the user for a new User Agent string -void SettingsComponent::ChangeUserAgent() { - TextInputDialog dialog( - m_appWindow->GetMainWindow(), - L"User Agent", - L"User agent:", - L"Enter user agent, or leave blank to restore default.", - m_changeUserAgent - ? m_overridingUserAgent.c_str() - : L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3818.0 Safari/537.36 Edg/77.0.188.0"); - if (dialog.confirmed) - { - SetUserAgent(dialog.input); - } -} - -// Register a WebResourceRequested handler which adds a custom User-Agent -// HTTP header to all requests. -void SettingsComponent::SetUserAgent(const std::wstring& userAgent) -{ - if (m_changeUserAgent) - { - CHECK_FAILURE(m_webView->remove_WebResourceRequested( - m_webResourceRequestedTokenForUserAgent)); - } - m_overridingUserAgent = userAgent; - if (m_overridingUserAgent.empty()) - { - m_changeUserAgent = false; - } - else - { - m_changeUserAgent = true; - // Register a handler for the WebResourceRequested event. - // This handler adds a User-Agent HTTP header to the request, - // then lets the request continue normally. - m_webView->AddWebResourceRequestedFilter(L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL); - m_webView->add_WebResourceRequested( - Callback( - [this](ICoreWebView2* sender, ICoreWebView2WebResourceRequestedEventArgs* args) { - wil::com_ptr request; - CHECK_FAILURE(args->get_Request(&request)); - wil::com_ptr requestHeaders; - CHECK_FAILURE(request->get_Headers(&requestHeaders)); - requestHeaders->SetHeader(L"User-Agent", m_overridingUserAgent.c_str()); - - return S_OK; - }) - .Get(), - &m_webResourceRequestedTokenForUserAgent); - } -} - -void SettingsComponent::CompleteScriptDialogDeferral() -{ - if (m_completeDeferredDialog) - { - m_completeDeferredDialog(); - m_completeDeferredDialog = nullptr; - } -} - -SettingsComponent::~SettingsComponent() -{ - m_webView->remove_NavigationStarting(m_navigationStartingToken); - m_webView->remove_FrameNavigationStarting(m_frameNavigationStartingToken); - m_webView->remove_WebResourceRequested(m_webResourceRequestedTokenForImageBlocking); - m_webView->remove_WebResourceRequested(m_webResourceRequestedTokenForUserAgent); - m_webView->remove_ScriptDialogOpening(m_scriptDialogOpeningToken); - m_webView->remove_PermissionRequested(m_permissionRequestedToken); -} - -// Take advantage of urlmon's URI library to parse a URI -static wil::unique_bstr GetDomainOfUri(PWSTR uri) -{ - wil::com_ptr uriObject; - CreateUri(uri, - Uri_CREATE_CANONICALIZE | Uri_CREATE_NO_DECODE_EXTRA_INFO, - 0, &uriObject); - wil::unique_bstr domain; - uriObject->GetHost(&domain); - return domain; -} - -static PCWSTR NameOfPermissionKind(COREWEBVIEW2_PERMISSION_KIND kind) -{ - switch (kind) - { - case COREWEBVIEW2_PERMISSION_KIND_MICROPHONE: - return L"Microphone"; - case COREWEBVIEW2_PERMISSION_KIND_CAMERA: - return L"Camera"; - case COREWEBVIEW2_PERMISSION_KIND_GEOLOCATION: - return L"Geolocation"; - case COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS: - return L"Notifications"; - case COREWEBVIEW2_PERMISSION_KIND_OTHER_SENSORS: - return L"Generic Sensors"; - case COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ: - return L"Clipboard Read"; - default: - return L"Unknown resources"; - } -} +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "SettingsComponent.h" + +#include "CheckFailure.h" +#include "TextInputDialog.h" + +using namespace Microsoft::WRL; + +// Some utility functions +static wil::unique_bstr GetDomainOfUri(PWSTR uri); +static PCWSTR NameOfPermissionKind(COREWEBVIEW2_PERMISSION_KIND kind); + +SettingsComponent::SettingsComponent( + AppWindow* appWindow, ICoreWebView2Environment* environment, SettingsComponent* old) + : m_appWindow(appWindow), m_webViewEnvironment(environment), + m_webView(appWindow->GetWebView()) +{ + CHECK_FAILURE(m_webView->get_Settings(&m_settings)); + + // Copy old settings if desired + if (old) + { + BOOL setting; + CHECK_FAILURE(old->m_settings->get_IsScriptEnabled(&setting)); + CHECK_FAILURE(m_settings->put_IsScriptEnabled(setting)); + CHECK_FAILURE(old->m_settings->get_IsWebMessageEnabled(&setting)); + CHECK_FAILURE(m_settings->put_IsWebMessageEnabled(setting)); + CHECK_FAILURE(old->m_settings->get_AreDefaultScriptDialogsEnabled(&setting)); + CHECK_FAILURE(m_settings->put_AreDefaultScriptDialogsEnabled(setting)); + CHECK_FAILURE(old->m_settings->get_IsStatusBarEnabled(&setting)); + CHECK_FAILURE(m_settings->put_IsStatusBarEnabled(setting)); + CHECK_FAILURE(old->m_settings->get_AreDevToolsEnabled(&setting)); + CHECK_FAILURE(m_settings->put_AreDevToolsEnabled(setting)); + SetBlockImages(old->m_blockImages); + SetUserAgent(old->m_overridingUserAgent); + m_deferScriptDialogs = old->m_deferScriptDialogs; + m_isScriptEnabled = old->m_isScriptEnabled; + m_blockedSitesSet = old->m_blockedSitesSet; + m_blockedSites = std::move(old->m_blockedSites); + m_overridingUserAgent = std::move(old->m_overridingUserAgent); + } + + //! [NavigationStarting] + // Register a handler for the NavigationStarting event. + // This handler will check the domain being navigated to, and if the domain + // matches a list of blocked sites, it will cancel the navigation and + // possibly display a warning page. It will also disable JavaScript on + // selected websites. + CHECK_FAILURE(m_webView->add_NavigationStarting( + Callback( + [this](ICoreWebView2* sender, + ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT + { + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(args->get_Uri(&uri)); + + if (ShouldBlockUri(uri.get())) + { + CHECK_FAILURE(args->put_Cancel(true)); + + // If the user clicked a link to navigate, show a warning page. + BOOL userInitiated; + CHECK_FAILURE(args->get_IsUserInitiated(&userInitiated)); + //! [NavigateToString] + static const PCWSTR htmlContent = + L"

Domain Blocked

" + L"

You've attempted to navigate to a domain in the blocked " + L"sites list. Press back to return to the previous page.

"; + CHECK_FAILURE(sender->NavigateToString(htmlContent)); + //! [NavigateToString] + } + //! [IsScriptEnabled] + // Changes to settings will apply at the next navigation, which includes the + // navigation after a NavigationStarting event. We can use this to change + // settings according to what site we're visiting. + if (ShouldBlockScriptForUri(uri.get())) + { + m_settings->put_IsScriptEnabled(FALSE); + } + else + { + m_settings->put_IsScriptEnabled(m_isScriptEnabled); + } + //! [IsScriptEnabled] + return S_OK; + }).Get(), &m_navigationStartingToken)); + //! [NavigationStarting] + + //! [FrameNavigationStarting] + // Register a handler for the FrameNavigationStarting event. + // This handler will prevent a frame from navigating to a blocked domain. + CHECK_FAILURE(m_webView->add_FrameNavigationStarting( + Callback( + [this](ICoreWebView2* sender, + ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT + { + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(args->get_Uri(&uri)); + + if (ShouldBlockUri(uri.get())) + { + CHECK_FAILURE(args->put_Cancel(true)); + } + return S_OK; + }).Get(), &m_frameNavigationStartingToken)); + //! [FrameNavigationStarting] + + //! [ScriptDialogOpening] + // Register a handler for the ScriptDialogOpening event. + // This handler will set up a custom prompt dialog for the user, + // and may defer the event if the setting to defer dialogs is enabled. + CHECK_FAILURE(m_webView->add_ScriptDialogOpening( + Callback( + [this]( + ICoreWebView2* sender, + ICoreWebView2ScriptDialogOpeningEventArgs* args) -> HRESULT + { + wil::com_ptr eventArgs = args; + auto showDialog = [this, eventArgs] + { + wil::unique_cotaskmem_string uri; + COREWEBVIEW2_SCRIPT_DIALOG_KIND type; + wil::unique_cotaskmem_string message; + wil::unique_cotaskmem_string defaultText; + + CHECK_FAILURE(eventArgs->get_Uri(&uri)); + CHECK_FAILURE(eventArgs->get_Kind(&type)); + CHECK_FAILURE(eventArgs->get_Message(&message)); + CHECK_FAILURE(eventArgs->get_DefaultText(&defaultText)); + + std::wstring promptString = std::wstring(L"The page at '") + + uri.get() + L"' says:"; + TextInputDialog dialog( + m_appWindow->GetMainWindow(), + L"Script Dialog", + promptString.c_str(), + message.get(), + defaultText.get(), + /* readonly */ type != COREWEBVIEW2_SCRIPT_DIALOG_KIND_PROMPT); + if (dialog.confirmed) + { + CHECK_FAILURE(eventArgs->put_ResultText(dialog.input.c_str())); + CHECK_FAILURE(eventArgs->Accept()); + } + }; + + if (m_deferScriptDialogs) + { + wil::com_ptr deferral; + CHECK_FAILURE(args->GetDeferral(&deferral)); + m_completeDeferredDialog = [showDialog, deferral] + { + showDialog(); + CHECK_FAILURE(deferral->Complete()); + }; + } + else + { + showDialog(); + } + + return S_OK; + }).Get(), &m_scriptDialogOpeningToken)); + //! [ScriptDialogOpening] + + //! [PermissionRequested] + // Register a handler for the PermissionRequested event. + // This handler prompts the user to allow or deny the request. + CHECK_FAILURE(m_webView->add_PermissionRequested( + Callback( + [this]( + ICoreWebView2* sender, + ICoreWebView2PermissionRequestedEventArgs* args) -> HRESULT + { + wil::unique_cotaskmem_string uri; + COREWEBVIEW2_PERMISSION_KIND kind = COREWEBVIEW2_PERMISSION_KIND_UNKNOWN_PERMISSION; + BOOL userInitiated = FALSE; + + CHECK_FAILURE(args->get_Uri(&uri)); + CHECK_FAILURE(args->get_PermissionKind(&kind)); + CHECK_FAILURE(args->get_IsUserInitiated(&userInitiated)); + + std::wstring message = L"Do you want to grant permission for "; + message += NameOfPermissionKind(kind); + message += L" to the website at "; + message += uri.get(); + message += L"?\n\n"; + message += (userInitiated + ? L"This request came from a user gesture." + : L"This request did not come from a user gesture."); + + int response = MessageBox(nullptr, message.c_str(), L"Permission Request", + MB_YESNOCANCEL | MB_ICONWARNING); + + COREWEBVIEW2_PERMISSION_STATE state = + response == IDYES ? COREWEBVIEW2_PERMISSION_STATE_ALLOW + : response == IDNO ? COREWEBVIEW2_PERMISSION_STATE_DENY + : COREWEBVIEW2_PERMISSION_STATE_DEFAULT; + CHECK_FAILURE(args->put_State(state)); + + return S_OK; + }).Get(), &m_permissionRequestedToken)); + //! [PermissionRequested] +} + +bool SettingsComponent::HandleWindowMessage( + HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam, + LRESULT* result) +{ + if (message == WM_COMMAND) + { + switch (LOWORD(wParam)) + { + case ID_BLOCKEDSITES: + { + ChangeBlockedSites(); + return true; + } + case ID_SETTINGS_SETUSERAGENT: + { + ChangeUserAgent(); + return true; + } + case IDM_TOGGLE_JAVASCRIPT: + { + m_isScriptEnabled = !m_isScriptEnabled; + m_settings->put_IsScriptEnabled(m_isScriptEnabled); + MessageBox(nullptr, + (std::wstring(L"JavaScript will be ") + + (m_isScriptEnabled ? L"enabled" : L"disabled") + + L" after the next navigation.").c_str(), + L"Settings change", MB_OK); + return true; + } + case IDM_TOGGLE_WEB_MESSAGING: + { + BOOL isWebMessageEnabled; + m_settings->get_IsWebMessageEnabled(&isWebMessageEnabled); + m_settings->put_IsWebMessageEnabled(!isWebMessageEnabled); + MessageBox(nullptr, + (std::wstring(L"Web Messaging will be ") + + (!isWebMessageEnabled ? L"enabled" : L"disabled") + + L" after the next navigation.").c_str(), + L"Settings change", MB_OK); + return true; + } + case ID_SETTINGS_STATUS_BAR_ENABLED: + { + BOOL isStatusBarEnabled; + m_settings->get_IsStatusBarEnabled(&isStatusBarEnabled); + m_settings->put_IsStatusBarEnabled(!isStatusBarEnabled); + MessageBox(nullptr, + (std::wstring(L"Status bar will be ") + + + (!isStatusBarEnabled ? L"enabled" : L"disabled") + + L" after the next navigation.").c_str(), + L"Settings change", MB_OK); + return true; + } + case ID_SETTINGS_DEV_TOOLS_ENABLED: + { + BOOL areDevToolsEnabled = FALSE; + m_settings->get_AreDevToolsEnabled(&areDevToolsEnabled); + m_settings->put_AreDevToolsEnabled(!areDevToolsEnabled); + MessageBox(nullptr, + (std::wstring(L"Dev tools will be ") + + (!areDevToolsEnabled ? L"enabled" : L"disabled") + + L" after the next navigation.").c_str(), + L"Settings change", MB_OK); + return true; + } + case IDM_USE_DEFAULT_SCRIPT_DIALOGS: + { + BOOL defaultCurrentlyEnabled; + m_settings->get_AreDefaultScriptDialogsEnabled(&defaultCurrentlyEnabled); + if (!defaultCurrentlyEnabled) + { + m_settings->put_AreDefaultScriptDialogsEnabled(TRUE); + MessageBox(nullptr, + L"Default script dialogs will be used after the next navigation.", + L"Settings change", MB_OK); + } + return true; + } + case IDM_USE_CUSTOM_SCRIPT_DIALOGS: + { + BOOL defaultCurrentlyEnabled; + m_settings->get_AreDefaultScriptDialogsEnabled(&defaultCurrentlyEnabled); + if (defaultCurrentlyEnabled) + { + m_settings->put_AreDefaultScriptDialogsEnabled(FALSE); + m_deferScriptDialogs = false; + MessageBox(nullptr, + L"Custom script dialogs without deferral will be used after the next navigation.", + L"Settings change", MB_OK); + } + else if (m_deferScriptDialogs) + { + m_deferScriptDialogs = false; + MessageBox(nullptr, + L"Custom script dialogs without deferral will be used now.", + L"Settings change", MB_OK); + } + return true; + } + case IDM_USE_DEFERRED_SCRIPT_DIALOGS: + { + BOOL defaultCurrentlyEnabled; + m_settings->get_AreDefaultScriptDialogsEnabled(&defaultCurrentlyEnabled); + if (defaultCurrentlyEnabled) + { + m_settings->put_AreDefaultScriptDialogsEnabled(FALSE); + m_deferScriptDialogs = true; + MessageBox(nullptr, + L"Custom script dialogs with deferral will be used after the next navigation.", + L"Settings change", MB_OK); + } + else if (!m_deferScriptDialogs) + { + m_deferScriptDialogs = true; + MessageBox(nullptr, + L"Custom script dialogs with deferral will be used now.", + L"Settings change", MB_OK); + } + return true; + } + case IDM_COMPLETE_JAVASCRIPT_DIALOG: + { + CompleteScriptDialogDeferral(); + return true; + } + case ID_SETTINGS_BLOCKALLIMAGES: + { + SetBlockImages(!m_blockImages); + MessageBox(nullptr, + (std::wstring(L"Image blocking has been ") + + (m_blockImages ? L"enabled." : L"disabled.")) + .c_str(), + L"Settings change", MB_OK); + return true; + } + case ID_SETTINGS_CONTEXT_MENUS_ENABLED: + { + //! [DisableContextMenu] + BOOL allowContextMenus; + CHECK_FAILURE(m_settings->get_AreDefaultContextMenusEnabled( + &allowContextMenus)); + if (allowContextMenus) { + CHECK_FAILURE(m_settings->put_AreDefaultContextMenusEnabled(FALSE)); + MessageBox(nullptr, + L"Context menus will be disabled after the next navigation.", + L"Settings change", MB_OK); + } + else { + CHECK_FAILURE(m_settings->put_AreDefaultContextMenusEnabled(TRUE)); + MessageBox(nullptr, + L"Context menus will be enabled after the next navigation.", + L"Settings change", MB_OK); + } + //! [DisableContextMenu] + return true; + } + case ID_SETTINGS_HOST_OBJECTS_ALLOWED: + { + //! [HostObjectsAccess] + BOOL allowHostObjects; + CHECK_FAILURE(m_settings->get_AreHostObjectsAllowed(&allowHostObjects)); + if (allowHostObjects) + { + CHECK_FAILURE(m_settings->put_AreHostObjectsAllowed(FALSE)); + MessageBox( + nullptr, L"Access to host objects will be denied after the next navigation.", + L"Settings change", MB_OK); + } + else + { + CHECK_FAILURE(m_settings->put_AreHostObjectsAllowed(TRUE)); + MessageBox( + nullptr, L"Access to host objects will be allowed after the next navigation.", + L"Settings change", MB_OK); + } + //! [HostObjectsAccess] + return true; + } + case ID_SETTINGS_ZOOM_ENABLED: + { + //! [DisableZoomControl] + BOOL zoomControlEnabled; + CHECK_FAILURE(m_settings->get_IsZoomControlEnabled(&zoomControlEnabled)); + if (zoomControlEnabled) + { + CHECK_FAILURE(m_settings->put_IsZoomControlEnabled(FALSE)); + MessageBox( + nullptr, L"Zoom control will be disabled after the next navigation.", L"Settings change", + MB_OK); + } + else + { + CHECK_FAILURE(m_settings->put_IsZoomControlEnabled(TRUE)); + MessageBox( + nullptr, L"Zoom control will be enabled after the next navigation.", L"Settings change", + MB_OK); + } + //! [DisableZoomControl] + return true; + } + case ID_SETTINGS_BUILTIN_ERROR_PAGE_ENABLED: + { + //! [BuiltInErrorPageEnabled] + BOOL enabled; + CHECK_FAILURE(m_settings->get_IsBuiltInErrorPageEnabled(&enabled)); + if (enabled) + { + CHECK_FAILURE(m_settings->put_IsBuiltInErrorPageEnabled(FALSE)); + MessageBox( + nullptr, L"Built-in error page will be disabled for future navigation.", + L"Settings change", MB_OK); + } + else + { + CHECK_FAILURE(m_settings->put_IsBuiltInErrorPageEnabled(TRUE)); + MessageBox( + nullptr, L"Built-in error page will be enabled for future navigation.", + L"Settings change", MB_OK); + } + //! [BuiltInErrorPageEnabled] + return true; + } + } + } + return false; +} + +// Prompt the user for a list of blocked domains +void SettingsComponent::ChangeBlockedSites() +{ + std::wstring blockedSitesString; + if (m_blockedSitesSet) + { + for (auto& site : m_blockedSites) + { + if (!blockedSitesString.empty()) + { + blockedSitesString += L";"; + } + blockedSitesString += site; + } + } + else + { + blockedSitesString = L"foo.com;bar.org"; + } + + TextInputDialog dialog( + m_appWindow->GetMainWindow(), + L"Blocked Sites", + L"Sites:", + L"Enter hostnames to block, separated by semicolons.", + blockedSitesString.c_str()); + if (dialog.confirmed) + { + m_blockedSitesSet = true; + m_blockedSites.clear(); + size_t begin = 0; + size_t end = 0; + while (end != std::wstring::npos) + { + end = dialog.input.find(L';', begin); + if (end != begin) + { + m_blockedSites.push_back(dialog.input.substr(begin, end - begin)); + } + begin = end + 1; + } + } +} + +// Check the URI's domain against the blocked sites list +bool SettingsComponent::ShouldBlockUri(PWSTR uri) +{ + wil::unique_bstr domain = GetDomainOfUri(uri); + + for (auto site = m_blockedSites.begin(); + site != m_blockedSites.end(); site++) + { + if (wcscmp(site->c_str(), domain.get()) == 0) + { + return true; + } + } + return false; +} + +// Decide whether a website should have script disabled. Since we're only using this +// for sample code and we don't actually want to break any websites, just return false. +bool SettingsComponent::ShouldBlockScriptForUri(PWSTR uri) +{ + return false; +} + +// Turn on or off image blocking by adding or removing a WebResourceRequested handler +// which selectively intercepts requests for images. +void SettingsComponent::SetBlockImages(bool blockImages) +{ + if (blockImages != m_blockImages) + { + m_blockImages = blockImages; + + //! [WebResourceRequested] + if (m_blockImages) + { + m_webView->AddWebResourceRequestedFilter(L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE); + CHECK_FAILURE(m_webView->add_WebResourceRequested( + Callback( + [this]( + ICoreWebView2* sender, + ICoreWebView2WebResourceRequestedEventArgs* args) { + COREWEBVIEW2_WEB_RESOURCE_CONTEXT resourceContext; + CHECK_FAILURE( + args->get_ResourceContext(&resourceContext)); + // Ensure that the type is image + if (resourceContext != COREWEBVIEW2_WEB_RESOURCE_CONTEXT_IMAGE) + { + return E_INVALIDARG; + } + // Override the response with an empty one to block the image. + // If put_Response is not called, the request will continue as normal. + wil::com_ptr response; + wil::com_ptr m_webViewExperimental = + m_webView.try_query(); + wil::com_ptr environment; + CHECK_FAILURE(m_webViewExperimental->get_Environment(&environment)); + CHECK_FAILURE(environment->CreateWebResourceResponse( + nullptr, 403 /*NoContent*/, L"Blocked", L"", &response)); + CHECK_FAILURE(args->put_Response(response.get())); + return S_OK; + }) + .Get(), + &m_webResourceRequestedTokenForImageBlocking)); + } + else + { + CHECK_FAILURE(m_webView->remove_WebResourceRequested( + m_webResourceRequestedTokenForImageBlocking)); + } + //! [WebResourceRequested] + } +} + +// Prompt the user for a new User Agent string +void SettingsComponent::ChangeUserAgent() { + TextInputDialog dialog( + m_appWindow->GetMainWindow(), + L"User Agent", + L"User agent:", + L"Enter user agent, or leave blank to restore default.", + m_changeUserAgent + ? m_overridingUserAgent.c_str() + : L"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3818.0 Safari/537.36 Edg/77.0.188.0"); + if (dialog.confirmed) + { + SetUserAgent(dialog.input); + } +} + +// Register a WebResourceRequested handler which adds a custom User-Agent +// HTTP header to all requests. +void SettingsComponent::SetUserAgent(const std::wstring& userAgent) +{ + if (m_changeUserAgent) + { + CHECK_FAILURE(m_webView->remove_WebResourceRequested( + m_webResourceRequestedTokenForUserAgent)); + } + m_overridingUserAgent = userAgent; + if (m_overridingUserAgent.empty()) + { + m_changeUserAgent = false; + } + else + { + m_changeUserAgent = true; + // Register a handler for the WebResourceRequested event. + // This handler adds a User-Agent HTTP header to the request, + // then lets the request continue normally. + m_webView->AddWebResourceRequestedFilter(L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL); + m_webView->add_WebResourceRequested( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2WebResourceRequestedEventArgs* args) { + wil::com_ptr request; + CHECK_FAILURE(args->get_Request(&request)); + wil::com_ptr requestHeaders; + CHECK_FAILURE(request->get_Headers(&requestHeaders)); + requestHeaders->SetHeader(L"User-Agent", m_overridingUserAgent.c_str()); + + return S_OK; + }) + .Get(), + &m_webResourceRequestedTokenForUserAgent); + } +} + +void SettingsComponent::CompleteScriptDialogDeferral() +{ + if (m_completeDeferredDialog) + { + m_completeDeferredDialog(); + m_completeDeferredDialog = nullptr; + } +} + +SettingsComponent::~SettingsComponent() +{ + m_webView->remove_NavigationStarting(m_navigationStartingToken); + m_webView->remove_FrameNavigationStarting(m_frameNavigationStartingToken); + m_webView->remove_WebResourceRequested(m_webResourceRequestedTokenForImageBlocking); + m_webView->remove_WebResourceRequested(m_webResourceRequestedTokenForUserAgent); + m_webView->remove_ScriptDialogOpening(m_scriptDialogOpeningToken); + m_webView->remove_PermissionRequested(m_permissionRequestedToken); +} + +// Take advantage of urlmon's URI library to parse a URI +static wil::unique_bstr GetDomainOfUri(PWSTR uri) +{ + wil::com_ptr uriObject; + CreateUri(uri, + Uri_CREATE_CANONICALIZE | Uri_CREATE_NO_DECODE_EXTRA_INFO, + 0, &uriObject); + wil::unique_bstr domain; + uriObject->GetHost(&domain); + return domain; +} + +static PCWSTR NameOfPermissionKind(COREWEBVIEW2_PERMISSION_KIND kind) +{ + switch (kind) + { + case COREWEBVIEW2_PERMISSION_KIND_MICROPHONE: + return L"Microphone"; + case COREWEBVIEW2_PERMISSION_KIND_CAMERA: + return L"Camera"; + case COREWEBVIEW2_PERMISSION_KIND_GEOLOCATION: + return L"Geolocation"; + case COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS: + return L"Notifications"; + case COREWEBVIEW2_PERMISSION_KIND_OTHER_SENSORS: + return L"Generic Sensors"; + case COREWEBVIEW2_PERMISSION_KIND_CLIPBOARD_READ: + return L"Clipboard Read"; + default: + return L"Unknown resources"; + } +} diff --git a/SampleApps/WebView2APISample/Toolbar.cpp b/SampleApps/WebView2APISample/Toolbar.cpp index 4b9fc74d..083b29db 100644 --- a/SampleApps/WebView2APISample/Toolbar.cpp +++ b/SampleApps/WebView2APISample/Toolbar.cpp @@ -1,150 +1,150 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "stdafx.h" - -#include "Toolbar.h" - -#include "AppWindow.h" -#include "resource.h" - -Toolbar::Toolbar() : m_items{ Item_LAST } {} - -Toolbar::~Toolbar() -{ - if (m_font) - { - DeleteObject(m_font); - } -} - -void Toolbar::Initialize(AppWindow* appWindow) -{ - m_appWindow = appWindow; - HWND mainWindow = m_appWindow->GetMainWindow(); - - m_items[Item_BackButton] = CreateWindow( - L"button", L"Back", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, - mainWindow, (HMENU)IDE_BACK, nullptr, 0); - m_items[Item_ForwardButton] = CreateWindow( - L"button", L"Forward", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, - mainWindow, (HMENU)IDE_FORWARD, nullptr, 0); - m_items[Item_ReloadButton] = CreateWindow( - L"button", L"Reload", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, - mainWindow, (HMENU)IDE_ADDRESSBAR_RELOAD, nullptr, 0); - m_items[Item_CancelButton] = CreateWindow( - L"button", L"Cancel", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, - mainWindow, (HMENU)IDE_CANCEL, nullptr, 0); - m_items[Item_AddressBar] = CreateWindow( - L"edit", nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, - mainWindow, (HMENU)IDE_ADDRESSBAR, nullptr, 0); - m_items[Item_GoButton] = CreateWindow( - L"button", L"Go", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | BS_DEFPUSHBUTTON, - 0, 0, 0, 0, mainWindow, (HMENU)IDE_ADDRESSBAR_GO, nullptr, 0); - - UpdateDpiAndTextScale(); - RECT availableBounds = { 0 }; - GetClientRect(mainWindow, &availableBounds); - Resize(availableBounds); - - DisableAllItems(); -} - -void Toolbar::SetItemEnabled(Item item, bool enabled) -{ - EnableWindow(m_items[item], enabled); -} - -void Toolbar::DisableAllItems() -{ - for (HWND hwnd : m_items) - { - EnableWindow(hwnd, FALSE); - } -} - -HWND Toolbar::GetItem(Item item) const -{ - return m_items[item]; -} - -const std::vector& Toolbar::GetItems() const -{ - return m_items; -} - -RECT Toolbar::Resize(RECT availableBounds) -{ - const int clientWidth = availableBounds.right - availableBounds.left; - const int clientHeight = availableBounds.bottom - availableBounds.top; - const float dpiScale = m_appWindow->GetDpiScale(); - const int clientLogicalWidth = clientWidth / dpiScale; - const int itemHeight = 32 * dpiScale; - - int nextOffsetX = 0; - - for (Item item = Item_BackButton; item < Item_LAST; item = Item(item + 1)) - { - int itemWidth = GetItemLogicalWidth(item, clientLogicalWidth) * dpiScale; - SetWindowPos(m_items[item], nullptr, nextOffsetX, 0, itemWidth, itemHeight, - SWP_NOZORDER | SWP_NOACTIVATE); - nextOffsetX += itemWidth; - } - - return { 0, itemHeight, clientWidth, clientHeight }; -} - -void Toolbar::UpdateDpiAndTextScale() -{ - UpdateFont(); - for (Item item = Item_BackButton; item < Item_LAST; item = Item(item + 1)) - { - SendMessage(m_items[item], WM_SETFONT, (WPARAM)m_font, FALSE); - } -} - -int Toolbar::GetItemLogicalWidth(Item item, int clientLogicalWidth) const -{ - static const int s_itemButtonLogicalWidth = 64; - - int itemLogicalWidth = 0; - switch (item) - { - case Item_BackButton: - case Item_ForwardButton: - case Item_ReloadButton: - case Item_CancelButton: - case Item_GoButton: - itemLogicalWidth = s_itemButtonLogicalWidth; - break; - case Item_AddressBar: - itemLogicalWidth = clientLogicalWidth - s_itemButtonLogicalWidth * (Item_LAST - 1); - break; - default: - FAIL_FAST(); - } - return itemLogicalWidth; -} - -void Toolbar::UpdateFont() -{ - static const WCHAR s_fontName[] = L"WebView2APISample Font"; - if (m_font) - { - DeleteObject(m_font); - } - LOGFONT logFont; - GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &logFont); - double dpiScale = m_appWindow->GetDpiScale(); -#ifdef USE_WEBVIEW2_WIN10 - double textScale = m_appWindow->GetTextScale(); - logFont.lfHeight *= dpiScale * textScale; - logFont.lfWidth *= dpiScale * textScale; -#else - logFont.lfHeight *= dpiScale; - logFont.lfWidth *= dpiScale; -#endif - StringCchCopy(logFont.lfFaceName, ARRAYSIZE(logFont.lfFaceName), s_fontName); - m_font = CreateFontIndirect(&logFont); -} +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "Toolbar.h" + +#include "AppWindow.h" +#include "resource.h" + +Toolbar::Toolbar() : m_items{ Item_LAST } {} + +Toolbar::~Toolbar() +{ + if (m_font) + { + DeleteObject(m_font); + } +} + +void Toolbar::Initialize(AppWindow* appWindow) +{ + m_appWindow = appWindow; + HWND mainWindow = m_appWindow->GetMainWindow(); + + m_items[Item_BackButton] = CreateWindow( + L"button", L"Back", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, + mainWindow, (HMENU)IDE_BACK, nullptr, 0); + m_items[Item_ForwardButton] = CreateWindow( + L"button", L"Forward", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, + mainWindow, (HMENU)IDE_FORWARD, nullptr, 0); + m_items[Item_ReloadButton] = CreateWindow( + L"button", L"Reload", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, + mainWindow, (HMENU)IDE_ADDRESSBAR_RELOAD, nullptr, 0); + m_items[Item_CancelButton] = CreateWindow( + L"button", L"Cancel", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, + mainWindow, (HMENU)IDE_CANCEL, nullptr, 0); + m_items[Item_AddressBar] = CreateWindow( + L"edit", nullptr, WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 0, 0, 0, 0, + mainWindow, (HMENU)IDE_ADDRESSBAR, nullptr, 0); + m_items[Item_GoButton] = CreateWindow( + L"button", L"Go", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | BS_DEFPUSHBUTTON, + 0, 0, 0, 0, mainWindow, (HMENU)IDE_ADDRESSBAR_GO, nullptr, 0); + + UpdateDpiAndTextScale(); + RECT availableBounds = { 0 }; + GetClientRect(mainWindow, &availableBounds); + Resize(availableBounds); + + DisableAllItems(); +} + +void Toolbar::SetItemEnabled(Item item, bool enabled) +{ + EnableWindow(m_items[item], enabled); +} + +void Toolbar::DisableAllItems() +{ + for (HWND hwnd : m_items) + { + EnableWindow(hwnd, FALSE); + } +} + +HWND Toolbar::GetItem(Item item) const +{ + return m_items[item]; +} + +const std::vector& Toolbar::GetItems() const +{ + return m_items; +} + +RECT Toolbar::Resize(RECT availableBounds) +{ + const int clientWidth = availableBounds.right - availableBounds.left; + const int clientHeight = availableBounds.bottom - availableBounds.top; + const float dpiScale = m_appWindow->GetDpiScale(); + const int clientLogicalWidth = clientWidth / dpiScale; + const int itemHeight = 32 * dpiScale; + + int nextOffsetX = 0; + + for (Item item = Item_BackButton; item < Item_LAST; item = Item(item + 1)) + { + int itemWidth = GetItemLogicalWidth(item, clientLogicalWidth) * dpiScale; + SetWindowPos(m_items[item], nullptr, nextOffsetX, 0, itemWidth, itemHeight, + SWP_NOZORDER | SWP_NOACTIVATE); + nextOffsetX += itemWidth; + } + + return { 0, itemHeight, clientWidth, clientHeight }; +} + +void Toolbar::UpdateDpiAndTextScale() +{ + UpdateFont(); + for (Item item = Item_BackButton; item < Item_LAST; item = Item(item + 1)) + { + SendMessage(m_items[item], WM_SETFONT, (WPARAM)m_font, FALSE); + } +} + +int Toolbar::GetItemLogicalWidth(Item item, int clientLogicalWidth) const +{ + static const int s_itemButtonLogicalWidth = 64; + + int itemLogicalWidth = 0; + switch (item) + { + case Item_BackButton: + case Item_ForwardButton: + case Item_ReloadButton: + case Item_CancelButton: + case Item_GoButton: + itemLogicalWidth = s_itemButtonLogicalWidth; + break; + case Item_AddressBar: + itemLogicalWidth = clientLogicalWidth - s_itemButtonLogicalWidth * (Item_LAST - 1); + break; + default: + FAIL_FAST(); + } + return itemLogicalWidth; +} + +void Toolbar::UpdateFont() +{ + static const WCHAR s_fontName[] = L"WebView2APISample Font"; + if (m_font) + { + DeleteObject(m_font); + } + LOGFONT logFont; + GetObject(GetStockObject(SYSTEM_FONT), sizeof(LOGFONT), &logFont); + double dpiScale = m_appWindow->GetDpiScale(); +#ifdef USE_WEBVIEW2_WIN10 + double textScale = m_appWindow->GetTextScale(); + logFont.lfHeight *= dpiScale * textScale; + logFont.lfWidth *= dpiScale * textScale; +#else + logFont.lfHeight *= dpiScale; + logFont.lfWidth *= dpiScale; +#endif + StringCchCopy(logFont.lfFaceName, ARRAYSIZE(logFont.lfFaceName), s_fontName); + m_font = CreateFontIndirect(&logFont); +} diff --git a/SampleApps/WebView2APISample/ViewComponent.cpp b/SampleApps/WebView2APISample/ViewComponent.cpp index 539a56d9..c16f3c60 100644 --- a/SampleApps/WebView2APISample/ViewComponent.cpp +++ b/SampleApps/WebView2APISample/ViewComponent.cpp @@ -1,672 +1,694 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "stdafx.h" - -#include "ViewComponent.h" -#include "DCompTargetImpl.h" - -#include -#include -#include -#ifdef USE_WEBVIEW2_WIN10 -#include -#endif - -#include "CheckFailure.h" - -using namespace Microsoft::WRL; -namespace numerics = winrt::Windows::Foundation::Numerics; -static D2D1_MATRIX_4X4_F Convert3x2MatrixTo4x4Matrix(D2D1_MATRIX_3X2_F* matrix3x2); - -ViewComponent::ViewComponent( - AppWindow* appWindow, - IDCompositionDevice* dcompDevice, -#ifdef USE_WEBVIEW2_WIN10 - winrtComp::Compositor wincompCompositor, -#endif - bool isDcompTargetMode) - : m_appWindow(appWindow), m_controller(appWindow->GetWebViewController()), - m_webView(appWindow->GetWebView()), m_dcompDevice(dcompDevice), -#ifdef USE_WEBVIEW2_WIN10 - m_wincompCompositor(wincompCompositor), -#endif - m_isDcompTargetMode(isDcompTargetMode) -{ - //! [ZoomFactorChanged] - // Register a handler for the ZoomFactorChanged event. - // This handler just announces the new level of zoom on the window's title bar. - CHECK_FAILURE(m_controller->add_ZoomFactorChanged( - Callback( - [this](ICoreWebView2Controller* sender, IUnknown* args) -> HRESULT { - double zoomFactor; - CHECK_FAILURE(sender->get_ZoomFactor(&zoomFactor)); - - std::wstring message = L"WebView2APISample (Zoom: " + - std::to_wstring(int(zoomFactor * 100)) + L"%)"; - SetWindowText(m_appWindow->GetMainWindow(), message.c_str()); - return S_OK; - }) - .Get(), - &m_zoomFactorChangedToken)); - //! [ZoomFactorChanged] - - // Set up compositor if we're running in windowless mode - m_compositionController = m_controller.try_query(); - if (m_compositionController) - { - if (m_dcompDevice) - { - //! [SetRootVisualTarget] - // Set the host app visual that the WebView will connect its visual - // tree to. - BuildDCompTreeUsingVisual(); - if (m_isDcompTargetMode) - { - if (!m_dcompTarget) - { - m_dcompTarget = Make(this); - } - CHECK_FAILURE( - m_compositionController->put_RootVisualTarget(m_dcompTarget.get())); - } - else - { - CHECK_FAILURE( - m_compositionController->put_RootVisualTarget(m_dcompWebViewVisual.get())); - } - CHECK_FAILURE(m_dcompDevice->Commit()); - //! [SetRootVisualTarget] - } -#ifdef USE_WEBVIEW2_WIN10 - else if (m_wincompCompositor) - { - BuildWinCompVisualTree(); - CHECK_FAILURE(m_compositionController->put_RootVisualTarget(m_wincompWebViewVisual.as().get())); - } -#endif - else - { - FAIL_FAST(); - } - //! [CursorChanged] - // Register a handler for the CursorChanged event. - CHECK_FAILURE(m_compositionController->add_CursorChanged( - Callback( - [this](ICoreWebView2ExperimentalCompositionController* sender, IUnknown* args) - -> HRESULT { - HRESULT hr = S_OK; - HCURSOR cursor; - CHECK_FAILURE(sender->get_Cursor(&cursor)); - if (SUCCEEDED(hr)) - { - SetClassLongPtr( - m_appWindow->GetMainWindow(), GCLP_HCURSOR, (LONG_PTR)cursor); - } - return hr; - }) - .Get(), - &m_cursorChangedToken)); - //! [CursorChanged] - } -#ifdef USE_WEBVIEW2_WIN10 - else if (m_dcompDevice || m_wincompCompositor) -#else - else if (m_dcompDevice) -#endif - { - FAIL_FAST(); - } - ResizeWebView(); -} -bool ViewComponent::HandleWindowMessage( - HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) -{ - if (message == WM_COMMAND) - { - switch (LOWORD(wParam)) - { - case IDM_TOGGLE_VISIBILITY: - ToggleVisibility(); - return true; - case IDM_ZOOM_05: - SetZoomFactor(0.5f); - return true; - case IDM_ZOOM_10: - SetZoomFactor(1.0f); - return true; - case IDM_ZOOM_20: - SetZoomFactor(2.0f); - return true; - case IDM_SIZE_25: - SetSizeRatio(0.5f); - return true; - case IDM_SIZE_50: - SetSizeRatio(0.7071f); - return true; - case IDM_SIZE_75: - SetSizeRatio(0.866f); - return true; - case IDM_SIZE_100: - SetSizeRatio(1.0f); - return true; - case IDM_TRANSFORM_NONE: - SetTransform(TransformType::kIdentity); - return true; - case IDM_TRANSFORM_ROTATE_30DEG: - SetTransform(TransformType::kRotate30Deg); - return true; - case IDM_TRANSFORM_ROTATE_60DEG_DIAG: - SetTransform(TransformType::kRotate60DegDiagonally); - return true; - case IDM_SCALE_50: - SetScale(0.5f); - return true; - case IDM_SCALE_100: - SetScale(1.0f); - return true; - case IDM_SCALE_125: - SetScale(1.25f); - return true; - case IDM_SCALE_150: - SetScale(1.5f); - return true; - case IDM_GET_WEBVIEW_BOUNDS: - ShowWebViewBounds(); - return true; - case IDM_GET_WEBVIEW_ZOOM: - ShowWebViewZoom(); - return true; - } - } - //! [ToggleIsVisibleOnMinimize] - if (message == WM_SYSCOMMAND) - { - if (wParam == SC_MINIMIZE) - { - // Hide the webview when the app window is minimized. - m_controller->put_IsVisible(FALSE); - } - else if (wParam == SC_RESTORE) - { - // When the app window is restored, show the webview - // (unless the user has toggle visibility off). - if (m_isVisible) - { - m_controller->put_IsVisible(TRUE); - } - } - } - //! [ToggleIsVisibleOnMinimize] - if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) || message == WM_MOUSELEAVE) - { - OnMouseMessage(message, wParam, lParam); - } - else if ( - message == WM_POINTERACTIVATE || message == WM_POINTERDOWN || - message == WM_POINTERENTER || message == WM_POINTERLEAVE || message == WM_POINTERUP || - message == WM_POINTERUPDATE) - { - OnPointerMessage(message, wParam, lParam); - } - //! [NotifyParentWindowPositionChanged] - if (message == WM_MOVE || message == WM_MOVING) - { - m_controller->NotifyParentWindowPositionChanged(); - return true; - } - //! [NotifyParentWindowPositionChanged] - return false; -} -//! [ToggleIsVisible] -void ViewComponent::ToggleVisibility() -{ - BOOL visible; - m_controller->get_IsVisible(&visible); - m_isVisible = !visible; - m_controller->put_IsVisible(m_isVisible); -} -//! [ToggleIsVisible] - -void ViewComponent::SetSizeRatio(float ratio) -{ - m_webViewRatio = ratio; - ResizeWebView(); -} - -void ViewComponent::SetZoomFactor(float zoom) -{ - m_webViewZoomFactor = zoom; - m_controller->put_ZoomFactor(zoom); -} - -void ViewComponent::SetBounds(RECT bounds) -{ - m_webViewBounds = bounds; - ResizeWebView(); -} - -//! [SetBoundsAndZoomFactor] -void ViewComponent::SetScale(float scale) -{ - RECT bounds; - CHECK_FAILURE(m_controller->get_Bounds(&bounds)); - double scaleChange = scale / m_webViewScale; - - bounds.bottom = LONG( - (bounds.bottom - bounds.top) * scaleChange + bounds.top); - bounds.right = LONG( - (bounds.right - bounds.left) * scaleChange + bounds.left); - - m_webViewScale = scale; - m_controller->SetBoundsAndZoomFactor(bounds, scale); -} -//! [SetBoundsAndZoomFactor] - -//! [ResizeWebView] -// Update the bounds of the WebView window to fit available space. -void ViewComponent::ResizeWebView() -{ - SIZE webViewSize = { - LONG((m_webViewBounds.right - m_webViewBounds.left) * m_webViewRatio * m_webViewScale), - LONG((m_webViewBounds.bottom - m_webViewBounds.top) * m_webViewRatio * m_webViewScale) }; - - RECT desiredBounds = m_webViewBounds; - desiredBounds.bottom = LONG( - webViewSize.cy + m_webViewBounds.top); - desiredBounds.right = LONG( - webViewSize.cx + m_webViewBounds.left); - - m_controller->put_Bounds(desiredBounds); - if (m_compositionController) - { - POINT webViewOffset = {m_webViewBounds.left, m_webViewBounds.top}; - - if (m_dcompDevice) - { - CHECK_FAILURE(m_dcompRootVisual->SetOffsetX(float(webViewOffset.x))); - CHECK_FAILURE(m_dcompRootVisual->SetOffsetY(float(webViewOffset.y))); - CHECK_FAILURE(m_dcompRootVisual->SetClip( - {0, 0, float(webViewSize.cx), float(webViewSize.cy)})); - CHECK_FAILURE(m_dcompDevice->Commit()); - } -#ifdef USE_WEBVIEW2_WIN10 - else if (m_wincompCompositor) - { - if (m_wincompRootVisual != nullptr) - { - numerics::float2 size = {static_cast(webViewSize.cx), - static_cast(webViewSize.cy)}; - m_wincompRootVisual.Size(size); - - numerics::float3 offset = {static_cast(webViewOffset.x), - static_cast(webViewOffset.y), 0.0f}; - m_wincompRootVisual.Offset(offset); - - winrtComp::IInsetClip insetClip = m_wincompCompositor.CreateInsetClip(); - m_wincompRootVisual.Clip(insetClip.as()); - } - } -#endif - } -} -//! [ResizeWebView] - -// Show the current bounds of the WebView. -void ViewComponent::ShowWebViewBounds() -{ - RECT bounds; - HRESULT result = m_controller->get_Bounds(&bounds); - if (SUCCEEDED(result)) - { - std::wstringstream message; - message << L"Left:\t" << bounds.left << L"\n" - << L"Top:\t" << bounds.top << L"\n" - << L"Right:\t" << bounds.right << L"\n" - << L"Bottom:\t" << bounds.bottom << std::endl; - MessageBox(nullptr, message.str().c_str(), L"WebView Bounds", MB_OK); - } -} - -// Show the current zoom factor of the WebView. -void ViewComponent::ShowWebViewZoom() -{ - double zoomFactor; - HRESULT result = m_controller->get_ZoomFactor(&zoomFactor); - if (SUCCEEDED(result)) - { - std::wstringstream message; - message << L"Zoom Factor:\t" << zoomFactor << std::endl; - MessageBox(nullptr, message.str().c_str(), L"WebView Zoom Factor", MB_OK); - } -} - -void ViewComponent::SetTransform(TransformType transformType) -{ - if (!m_compositionController) - { - MessageBox( - nullptr, - L"Setting transform is not supported in windowed mode." - "Choose a windowless mode for creation before trying to apply a transform.", - L"Applying transform failed.", MB_OK); - return; - } - - if (transformType == TransformType::kRotate30Deg) - { - D2D1_POINT_2F center = D2D1::Point2F( - (m_webViewBounds.right - m_webViewBounds.left) / 2.f, - (m_webViewBounds.bottom - m_webViewBounds.top) / 2.f); - m_webViewTransformMatrix = - Convert3x2MatrixTo4x4Matrix(&D2D1::Matrix3x2F::Rotation(30.0f, center)); - } - else if (transformType == TransformType::kRotate60DegDiagonally) - { - m_webViewTransformMatrix = D2D1::Matrix4x4F::RotationArbitraryAxis( - float(m_webViewBounds.right), float(m_webViewBounds.bottom), 0, 60); - } - else if (transformType == TransformType::kIdentity) - { - m_webViewTransformMatrix = D2D1::Matrix4x4F(); - } - -#ifdef USE_WEBVIEW2_WIN10 - if (m_dcompDevice && !m_wincompCompositor) -#else - if (m_dcompDevice) -#endif - { - wil::com_ptr dcompWebViewVisual3; - m_dcompWebViewVisual->QueryInterface(IID_PPV_ARGS(&dcompWebViewVisual3)); - CHECK_FAILURE(dcompWebViewVisual3->SetTransform(m_webViewTransformMatrix)); - CHECK_FAILURE(m_dcompDevice->Commit()); - } -#ifdef USE_WEBVIEW2_WIN10 - else if (m_wincompCompositor && !m_dcompDevice) - { - if (m_wincompWebViewVisual != nullptr) - { - m_wincompWebViewVisual.TransformMatrix( - *reinterpret_cast(&m_webViewTransformMatrix)); - } - } -#endif - else - { - FAIL_FAST(); - } -} - -static D2D1_MATRIX_4X4_F Convert3x2MatrixTo4x4Matrix(D2D1_MATRIX_3X2_F* matrix3x2) -{ - D2D1_MATRIX_4X4_F matrix4x4 = D2D1::Matrix4x4F(); - matrix4x4._11 = matrix3x2->m11; - matrix4x4._12 = matrix3x2->m12; - matrix4x4._21 = matrix3x2->m21; - matrix4x4._22 = matrix3x2->m22; - matrix4x4._41 = matrix3x2->dx; - matrix4x4._42 = matrix3x2->dy; - return matrix4x4; -} - -//! [SendMouseInput] -bool ViewComponent::OnMouseMessage(UINT message, WPARAM wParam, LPARAM lParam) -{ - // Manually relay mouse messages to the WebView -#ifdef USE_WEBVIEW2_WIN10 - if (m_dcompDevice || m_wincompCompositor) -#else - if (m_dcompDevice) -#endif - { - POINT point; - POINTSTOPOINT(point, lParam); - if (message == WM_MOUSEWHEEL || message == WM_MOUSEHWHEEL) - { - // Mouse wheel messages are delivered in screen coordinates. - // SendMouseInput expects client coordinates for the WebView, so convert - // the point from screen to client. - ::ScreenToClient(m_appWindow->GetMainWindow(), &point); - } - // Send the message to the WebView if the mouse location is inside the - // bounds of the WebView, if the message is telling the WebView the - // mouse has left the client area, or if we are currently capturing - // mouse events. - bool isMouseInWebView = PtInRect(&m_webViewBounds, point); - if (isMouseInWebView || message == WM_MOUSELEAVE || m_isCapturingMouse) - { - DWORD mouseData = 0; - - switch (message) - { - case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: - mouseData = GET_WHEEL_DELTA_WPARAM(wParam); - break; - case WM_XBUTTONDBLCLK: - case WM_XBUTTONDOWN: - case WM_XBUTTONUP: - mouseData = GET_XBUTTON_WPARAM(wParam); - break; - case WM_MOUSEMOVE: - if (!m_isTrackingMouse) - { - // WebView needs to know when the mouse leaves the client area - // so that it can dismiss hover popups. TrackMouseEvent will - // provide a notification when the mouse leaves the client area. - TrackMouseEvents(TME_LEAVE); - m_isTrackingMouse = true; - } - break; - case WM_MOUSELEAVE: - m_isTrackingMouse = false; - break; - } - - // We need to capture the mouse in case the user drags the - // mouse outside of the window bounds and we still need to send - // mouse messages to the WebView process. This is useful for - // scenarios like dragging the scroll bar or panning a map. - // This is very similar to the Pointer Message case where a - // press started inside of the WebView. - if (message == WM_LBUTTONDOWN || message == WM_MBUTTONDOWN || - message == WM_RBUTTONDOWN || message == WM_XBUTTONDOWN) - { - if (isMouseInWebView && ::GetCapture() != m_appWindow->GetMainWindow()) - { - m_isCapturingMouse = true; - ::SetCapture(m_appWindow->GetMainWindow()); - } - } - else if (message == WM_LBUTTONUP || message == WM_MBUTTONUP || - message == WM_RBUTTONUP || message == WM_XBUTTONUP) - { - if (::GetCapture() == m_appWindow->GetMainWindow()) - { - m_isCapturingMouse = false; - ::ReleaseCapture(); - } - } - - // Adjust the point from app client coordinates to webview client coordinates. - // WM_MOUSELEAVE messages don't have a point, so don't adjust the point. - if (message != WM_MOUSELEAVE) - { - point.x -= m_webViewBounds.left; - point.y -= m_webViewBounds.top; - } - - CHECK_FAILURE(m_compositionController->SendMouseInput( - static_cast(message), - static_cast(GET_KEYSTATE_WPARAM(wParam)), - mouseData, point)); - return true; - } - else if (message == WM_MOUSEMOVE && m_isTrackingMouse) - { - // When the mouse moves outside of the WebView, but still inside the app - // turn off mouse tracking and send the WebView a leave event. - m_isTrackingMouse = false; - TrackMouseEvents(TME_LEAVE | TME_CANCEL); - OnMouseMessage(WM_MOUSELEAVE, 0, 0); - } - } - return false; -} -//! [SendMouseInput] - -bool ViewComponent::OnPointerMessage(UINT message, WPARAM wParam, LPARAM lParam) -{ - bool handled = false; -#ifdef USE_WEBVIEW2_WIN10 - if (m_dcompDevice || m_wincompCompositor) -#else - if (m_dcompDevice) -#endif - { - POINT point; - POINTSTOPOINT(point, lParam); - UINT pointerId = GET_POINTERID_WPARAM(wParam); - - ::ScreenToClient(m_appWindow->GetMainWindow(), &point); - - bool pointerStartedInWebView = m_pointerIdsStartingInWebView.find(pointerId) != - m_pointerIdsStartingInWebView.end(); - // We want to send pointer input to the WebView for all pointers that either is in the - // WebView or started inside the WebView. For example, if a user started a page scroll - // inside of the WebView but dragged their finger outside of the WebView, we need to - // keep sending pointer events for those pointers. - if (PtInRect(&m_webViewBounds, point) || pointerStartedInWebView) - { - if (!pointerStartedInWebView && - (message == WM_POINTERENTER || message == WM_POINTERDOWN)) - { - m_pointerIdsStartingInWebView.insert(pointerId); - } - else if (message == WM_POINTERLEAVE) - { - m_pointerIdsStartingInWebView.erase(pointerId); - } - - handled = true; - wil::com_ptr pointer_info; - COREWEBVIEW2_MATRIX_4X4* webviewMatrix = - reinterpret_cast(&m_webViewTransformMatrix); - CHECK_FAILURE(m_compositionController->CreateCoreWebView2PointerInfoFromPointerId( - pointerId, m_appWindow->GetMainWindow(), *webviewMatrix, &pointer_info)); - CHECK_FAILURE(m_compositionController->SendPointerInput( - static_cast(message), pointer_info.get())); - } - } - return handled; -} - -void ViewComponent::TrackMouseEvents(DWORD mouseTrackingFlags) -{ - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(tme); - tme.dwFlags = mouseTrackingFlags; - tme.hwndTrack = m_appWindow->GetMainWindow(); - tme.dwHoverTime = 0; - ::TrackMouseEvent(&tme); -} - -//! [BuildDCompTree] -// Create host app visual that the WebView will connect to. -// - Create a IDCompositionTarget for the host window -// - Create a visual and set that as the IDCompositionTarget's root -// - Create another visual and add that to the IDCompositionTarget's root. -// This visual will be the visual root for the WebView. -void ViewComponent::BuildDCompTreeUsingVisual() -{ - CHECK_FAILURE_BOOL(m_dcompDevice != nullptr); - - if (m_dcompWebViewVisual == nullptr) - { - CHECK_FAILURE(m_dcompDevice->CreateTargetForHwnd( - m_appWindow->GetMainWindow(), TRUE, &m_dcompHwndTarget)); - CHECK_FAILURE(m_dcompDevice->CreateVisual(&m_dcompRootVisual)); - CHECK_FAILURE(m_dcompHwndTarget->SetRoot(m_dcompRootVisual.get())); - CHECK_FAILURE(m_dcompDevice->CreateVisual(&m_dcompWebViewVisual)); - CHECK_FAILURE(m_dcompRootVisual->AddVisual(m_dcompWebViewVisual.get(), TRUE, nullptr)); - } -} -//! [BuildDCompTree] - -void ViewComponent::DestroyDCompVisualTree() -{ - if (m_dcompWebViewVisual) - { - m_dcompWebViewVisual->RemoveAllVisuals(); - m_dcompWebViewVisual.reset(); - - m_dcompRootVisual->RemoveAllVisuals(); - m_dcompRootVisual.reset(); - - m_dcompHwndTarget->SetRoot(nullptr); - m_dcompHwndTarget.reset(); - - m_dcompDevice->Commit(); - } - - if (m_dcompTarget) - { - m_dcompTarget->RemoveOwnerRef(); - m_dcompTarget = nullptr; - } -} - -#ifdef USE_WEBVIEW2_WIN10 -void ViewComponent::BuildWinCompVisualTree() -{ - namespace abiComp = ABI::Windows::UI::Composition; - - if (m_wincompWebViewVisual == nullptr) - { - auto interop = m_wincompCompositor.as(); - winrt::check_hresult(interop->CreateDesktopWindowTarget( - m_appWindow->GetMainWindow(), false, - reinterpret_cast(winrt::put_abi(m_wincompHwndTarget)))); - - m_wincompRootVisual = m_wincompCompositor.CreateContainerVisual(); - m_wincompHwndTarget.Root(m_wincompRootVisual); - - m_wincompWebViewVisual = m_wincompCompositor.CreateContainerVisual(); - m_wincompRootVisual.Children().InsertAtTop(m_wincompWebViewVisual); - } -} - -void ViewComponent::DestroyWinCompVisualTree() -{ - if (m_wincompWebViewVisual != nullptr) - { - m_wincompWebViewVisual.Children().RemoveAll(); - m_wincompWebViewVisual = nullptr; - - m_wincompRootVisual.Children().RemoveAll(); - m_wincompRootVisual = nullptr; - - m_wincompHwndTarget.Root(nullptr); - m_wincompHwndTarget = nullptr; - } -} -#endif - -ViewComponent::~ViewComponent() -{ - m_controller->remove_ZoomFactorChanged(m_zoomFactorChangedToken); - if (m_compositionController) - { - m_compositionController->remove_CursorChanged(m_cursorChangedToken); - // If the webview closes because the AppWindow is closed (as opposed to being closed - // explicitly), this will no-op because in this case, the webview closes before the ViewComponent - // is destroyed. If the webview is closed explicitly, this will succeed. - m_compositionController->put_RootVisualTarget(nullptr); - DestroyDCompVisualTree(); -#ifdef USE_WEBVIEW2_WIN10 - DestroyWinCompVisualTree(); -#endif - } -} +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "stdafx.h" + +#include "ViewComponent.h" +#include "DCompTargetImpl.h" + +#include +#include +#include +#ifdef USE_WEBVIEW2_WIN10 +#include +#endif + +#include "CheckFailure.h" + +using namespace Microsoft::WRL; +namespace numerics = winrt::Windows::Foundation::Numerics; +static D2D1_MATRIX_4X4_F Convert3x2MatrixTo4x4Matrix(D2D1_MATRIX_3X2_F* matrix3x2); + +ViewComponent::ViewComponent( + AppWindow* appWindow, + IDCompositionDevice* dcompDevice, +#ifdef USE_WEBVIEW2_WIN10 + winrtComp::Compositor wincompCompositor, +#endif + bool isDcompTargetMode) + : m_appWindow(appWindow), m_controller(appWindow->GetWebViewController()), + m_webView(appWindow->GetWebView()), m_dcompDevice(dcompDevice), +#ifdef USE_WEBVIEW2_WIN10 + m_wincompCompositor(wincompCompositor), +#endif + m_isDcompTargetMode(isDcompTargetMode) +{ + //! [ZoomFactorChanged] + // Register a handler for the ZoomFactorChanged event. + // This handler just announces the new level of zoom on the window's title bar. + CHECK_FAILURE(m_controller->add_ZoomFactorChanged( + Callback( + [this](ICoreWebView2Controller* sender, IUnknown* args) -> HRESULT { + double zoomFactor; + CHECK_FAILURE(sender->get_ZoomFactor(&zoomFactor)); + + std::wstring message = L"WebView2APISample (Zoom: " + + std::to_wstring(int(zoomFactor * 100)) + L"%)"; + SetWindowText(m_appWindow->GetMainWindow(), message.c_str()); + return S_OK; + }) + .Get(), + &m_zoomFactorChangedToken)); + //! [ZoomFactorChanged] + + // Set up compositor if we're running in windowless mode + m_compositionController = m_controller.try_query(); + if (m_compositionController) + { + if (m_dcompDevice) + { + //! [SetRootVisualTarget] + // Set the host app visual that the WebView will connect its visual + // tree to. + BuildDCompTreeUsingVisual(); + if (m_isDcompTargetMode) + { + if (!m_dcompTarget) + { + m_dcompTarget = Make(this); + } + CHECK_FAILURE( + m_compositionController->put_RootVisualTarget(m_dcompTarget.get())); + } + else + { + CHECK_FAILURE( + m_compositionController->put_RootVisualTarget(m_dcompWebViewVisual.get())); + } + CHECK_FAILURE(m_dcompDevice->Commit()); + //! [SetRootVisualTarget] + } +#ifdef USE_WEBVIEW2_WIN10 + else if (m_wincompCompositor) + { + BuildWinCompVisualTree(); + CHECK_FAILURE(m_compositionController->put_RootVisualTarget(m_wincompWebViewVisual.as().get())); + } +#endif + else + { + FAIL_FAST(); + } + //! [CursorChanged] + // Register a handler for the CursorChanged event. + CHECK_FAILURE(m_compositionController->add_CursorChanged( + Callback( + [this](ICoreWebView2ExperimentalCompositionController* sender, IUnknown* args) + -> HRESULT { + HRESULT hr = S_OK; + HCURSOR cursor; + if (!m_useCursorId) + { + CHECK_FAILURE(sender->get_Cursor(&cursor)); + } + else + { + //! [SystemCursorId] + UINT32 cursorId; + wil::com_ptr compositionController2 = + m_controller.query(); + CHECK_FAILURE(compositionController2->get_SystemCursorId(&cursorId)); + cursor = ::LoadCursor(nullptr, MAKEINTRESOURCE(cursorId)); + if (cursor == nullptr) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + //! [SystemCursorId] + } + + if (SUCCEEDED(hr)) + { + SetClassLongPtr( + m_appWindow->GetMainWindow(), GCLP_HCURSOR, (LONG_PTR)cursor); + } + return hr; + }) + .Get(), + &m_cursorChangedToken)); + //! [CursorChanged] + } +#ifdef USE_WEBVIEW2_WIN10 + else if (m_dcompDevice || m_wincompCompositor) +#else + else if (m_dcompDevice) +#endif + { + FAIL_FAST(); + } + + ResizeWebView(); +} +bool ViewComponent::HandleWindowMessage( + HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) +{ + if (message == WM_COMMAND) + { + switch (LOWORD(wParam)) + { + case IDM_TOGGLE_VISIBILITY: + ToggleVisibility(); + return true; + case IDM_ZOOM_05: + SetZoomFactor(0.5f); + return true; + case IDM_ZOOM_10: + SetZoomFactor(1.0f); + return true; + case IDM_ZOOM_20: + SetZoomFactor(2.0f); + return true; + case IDM_SIZE_25: + SetSizeRatio(0.5f); + return true; + case IDM_SIZE_50: + SetSizeRatio(0.7071f); + return true; + case IDM_SIZE_75: + SetSizeRatio(0.866f); + return true; + case IDM_SIZE_100: + SetSizeRatio(1.0f); + return true; + case IDM_TRANSFORM_NONE: + SetTransform(TransformType::kIdentity); + return true; + case IDM_TRANSFORM_ROTATE_30DEG: + SetTransform(TransformType::kRotate30Deg); + return true; + case IDM_TRANSFORM_ROTATE_60DEG_DIAG: + SetTransform(TransformType::kRotate60DegDiagonally); + return true; + case IDM_SCALE_50: + SetScale(0.5f); + return true; + case IDM_SCALE_100: + SetScale(1.0f); + return true; + case IDM_SCALE_125: + SetScale(1.25f); + return true; + case IDM_SCALE_150: + SetScale(1.5f); + return true; + case IDM_GET_WEBVIEW_BOUNDS: + ShowWebViewBounds(); + return true; + case IDM_GET_WEBVIEW_ZOOM: + ShowWebViewZoom(); + return true; + case IDM_TOGGLE_CURSOR_HANDLING: + m_useCursorId = !m_useCursorId; + return true; + } + } + //! [ToggleIsVisibleOnMinimize] + if (message == WM_SYSCOMMAND) + { + if (wParam == SC_MINIMIZE) + { + // Hide the webview when the app window is minimized. + m_controller->put_IsVisible(FALSE); + } + else if (wParam == SC_RESTORE) + { + // When the app window is restored, show the webview + // (unless the user has toggle visibility off). + if (m_isVisible) + { + m_controller->put_IsVisible(TRUE); + } + } + } + //! [ToggleIsVisibleOnMinimize] + if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) || message == WM_MOUSELEAVE) + { + OnMouseMessage(message, wParam, lParam); + } + else if ( + message == WM_POINTERACTIVATE || message == WM_POINTERDOWN || + message == WM_POINTERENTER || message == WM_POINTERLEAVE || message == WM_POINTERUP || + message == WM_POINTERUPDATE) + { + OnPointerMessage(message, wParam, lParam); + } + //! [NotifyParentWindowPositionChanged] + if (message == WM_MOVE || message == WM_MOVING) + { + m_controller->NotifyParentWindowPositionChanged(); + return true; + } + //! [NotifyParentWindowPositionChanged] + return false; +} +//! [ToggleIsVisible] +void ViewComponent::ToggleVisibility() +{ + BOOL visible; + m_controller->get_IsVisible(&visible); + m_isVisible = !visible; + m_controller->put_IsVisible(m_isVisible); +} +//! [ToggleIsVisible] + +void ViewComponent::SetSizeRatio(float ratio) +{ + m_webViewRatio = ratio; + ResizeWebView(); +} + +void ViewComponent::SetZoomFactor(float zoom) +{ + m_webViewZoomFactor = zoom; + m_controller->put_ZoomFactor(zoom); +} + +void ViewComponent::SetBounds(RECT bounds) +{ + m_webViewBounds = bounds; + ResizeWebView(); +} + +//! [SetBoundsAndZoomFactor] +void ViewComponent::SetScale(float scale) +{ + RECT bounds; + CHECK_FAILURE(m_controller->get_Bounds(&bounds)); + double scaleChange = scale / m_webViewScale; + + bounds.bottom = LONG( + (bounds.bottom - bounds.top) * scaleChange + bounds.top); + bounds.right = LONG( + (bounds.right - bounds.left) * scaleChange + bounds.left); + + m_webViewScale = scale; + m_controller->SetBoundsAndZoomFactor(bounds, scale); +} +//! [SetBoundsAndZoomFactor] + +//! [ResizeWebView] +// Update the bounds of the WebView window to fit available space. +void ViewComponent::ResizeWebView() +{ + SIZE webViewSize = { + LONG((m_webViewBounds.right - m_webViewBounds.left) * m_webViewRatio * m_webViewScale), + LONG((m_webViewBounds.bottom - m_webViewBounds.top) * m_webViewRatio * m_webViewScale) }; + + RECT desiredBounds = m_webViewBounds; + desiredBounds.bottom = LONG( + webViewSize.cy + m_webViewBounds.top); + desiredBounds.right = LONG( + webViewSize.cx + m_webViewBounds.left); + + m_controller->put_Bounds(desiredBounds); + if (m_compositionController) + { + POINT webViewOffset = {m_webViewBounds.left, m_webViewBounds.top}; + + if (m_dcompDevice) + { + CHECK_FAILURE(m_dcompRootVisual->SetOffsetX(float(webViewOffset.x))); + CHECK_FAILURE(m_dcompRootVisual->SetOffsetY(float(webViewOffset.y))); + CHECK_FAILURE(m_dcompRootVisual->SetClip( + {0, 0, float(webViewSize.cx), float(webViewSize.cy)})); + CHECK_FAILURE(m_dcompDevice->Commit()); + } +#ifdef USE_WEBVIEW2_WIN10 + else if (m_wincompCompositor) + { + if (m_wincompRootVisual != nullptr) + { + numerics::float2 size = {static_cast(webViewSize.cx), + static_cast(webViewSize.cy)}; + m_wincompRootVisual.Size(size); + + numerics::float3 offset = {static_cast(webViewOffset.x), + static_cast(webViewOffset.y), 0.0f}; + m_wincompRootVisual.Offset(offset); + + winrtComp::IInsetClip insetClip = m_wincompCompositor.CreateInsetClip(); + m_wincompRootVisual.Clip(insetClip.as()); + } + } +#endif + } +} +//! [ResizeWebView] + +// Show the current bounds of the WebView. +void ViewComponent::ShowWebViewBounds() +{ + RECT bounds; + HRESULT result = m_controller->get_Bounds(&bounds); + if (SUCCEEDED(result)) + { + std::wstringstream message; + message << L"Left:\t" << bounds.left << L"\n" + << L"Top:\t" << bounds.top << L"\n" + << L"Right:\t" << bounds.right << L"\n" + << L"Bottom:\t" << bounds.bottom << std::endl; + MessageBox(nullptr, message.str().c_str(), L"WebView Bounds", MB_OK); + } +} + +// Show the current zoom factor of the WebView. +void ViewComponent::ShowWebViewZoom() +{ + double zoomFactor; + HRESULT result = m_controller->get_ZoomFactor(&zoomFactor); + if (SUCCEEDED(result)) + { + std::wstringstream message; + message << L"Zoom Factor:\t" << zoomFactor << std::endl; + MessageBox(nullptr, message.str().c_str(), L"WebView Zoom Factor", MB_OK); + } +} + +void ViewComponent::SetTransform(TransformType transformType) +{ + if (!m_compositionController) + { + MessageBox( + nullptr, + L"Setting transform is not supported in windowed mode." + "Choose a windowless mode for creation before trying to apply a transform.", + L"Applying transform failed.", MB_OK); + return; + } + + if (transformType == TransformType::kRotate30Deg) + { + D2D1_POINT_2F center = D2D1::Point2F( + (m_webViewBounds.right - m_webViewBounds.left) / 2.f, + (m_webViewBounds.bottom - m_webViewBounds.top) / 2.f); + m_webViewTransformMatrix = + Convert3x2MatrixTo4x4Matrix(&D2D1::Matrix3x2F::Rotation(30.0f, center)); + } + else if (transformType == TransformType::kRotate60DegDiagonally) + { + m_webViewTransformMatrix = D2D1::Matrix4x4F::RotationArbitraryAxis( + float(m_webViewBounds.right), float(m_webViewBounds.bottom), 0, 60); + } + else if (transformType == TransformType::kIdentity) + { + m_webViewTransformMatrix = D2D1::Matrix4x4F(); + } + +#ifdef USE_WEBVIEW2_WIN10 + if (m_dcompDevice && !m_wincompCompositor) +#else + if (m_dcompDevice) +#endif + { + wil::com_ptr dcompWebViewVisual3; + m_dcompWebViewVisual->QueryInterface(IID_PPV_ARGS(&dcompWebViewVisual3)); + CHECK_FAILURE(dcompWebViewVisual3->SetTransform(m_webViewTransformMatrix)); + CHECK_FAILURE(m_dcompDevice->Commit()); + } +#ifdef USE_WEBVIEW2_WIN10 + else if (m_wincompCompositor && !m_dcompDevice) + { + if (m_wincompWebViewVisual != nullptr) + { + m_wincompWebViewVisual.TransformMatrix( + *reinterpret_cast(&m_webViewTransformMatrix)); + } + } +#endif + else + { + FAIL_FAST(); + } +} + +static D2D1_MATRIX_4X4_F Convert3x2MatrixTo4x4Matrix(D2D1_MATRIX_3X2_F* matrix3x2) +{ + D2D1_MATRIX_4X4_F matrix4x4 = D2D1::Matrix4x4F(); + matrix4x4._11 = matrix3x2->m11; + matrix4x4._12 = matrix3x2->m12; + matrix4x4._21 = matrix3x2->m21; + matrix4x4._22 = matrix3x2->m22; + matrix4x4._41 = matrix3x2->dx; + matrix4x4._42 = matrix3x2->dy; + return matrix4x4; +} + +//! [SendMouseInput] +bool ViewComponent::OnMouseMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + // Manually relay mouse messages to the WebView +#ifdef USE_WEBVIEW2_WIN10 + if (m_dcompDevice || m_wincompCompositor) +#else + if (m_dcompDevice) +#endif + { + POINT point; + POINTSTOPOINT(point, lParam); + if (message == WM_MOUSEWHEEL || message == WM_MOUSEHWHEEL) + { + // Mouse wheel messages are delivered in screen coordinates. + // SendMouseInput expects client coordinates for the WebView, so convert + // the point from screen to client. + ::ScreenToClient(m_appWindow->GetMainWindow(), &point); + } + // Send the message to the WebView if the mouse location is inside the + // bounds of the WebView, if the message is telling the WebView the + // mouse has left the client area, or if we are currently capturing + // mouse events. + bool isMouseInWebView = PtInRect(&m_webViewBounds, point); + if (isMouseInWebView || message == WM_MOUSELEAVE || m_isCapturingMouse) + { + DWORD mouseData = 0; + + switch (message) + { + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + mouseData = GET_WHEEL_DELTA_WPARAM(wParam); + break; + case WM_XBUTTONDBLCLK: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: + mouseData = GET_XBUTTON_WPARAM(wParam); + break; + case WM_MOUSEMOVE: + if (!m_isTrackingMouse) + { + // WebView needs to know when the mouse leaves the client area + // so that it can dismiss hover popups. TrackMouseEvent will + // provide a notification when the mouse leaves the client area. + TrackMouseEvents(TME_LEAVE); + m_isTrackingMouse = true; + } + break; + case WM_MOUSELEAVE: + m_isTrackingMouse = false; + break; + } + + // We need to capture the mouse in case the user drags the + // mouse outside of the window bounds and we still need to send + // mouse messages to the WebView process. This is useful for + // scenarios like dragging the scroll bar or panning a map. + // This is very similar to the Pointer Message case where a + // press started inside of the WebView. + if (message == WM_LBUTTONDOWN || message == WM_MBUTTONDOWN || + message == WM_RBUTTONDOWN || message == WM_XBUTTONDOWN) + { + if (isMouseInWebView && ::GetCapture() != m_appWindow->GetMainWindow()) + { + m_isCapturingMouse = true; + ::SetCapture(m_appWindow->GetMainWindow()); + } + } + else if (message == WM_LBUTTONUP || message == WM_MBUTTONUP || + message == WM_RBUTTONUP || message == WM_XBUTTONUP) + { + if (::GetCapture() == m_appWindow->GetMainWindow()) + { + m_isCapturingMouse = false; + ::ReleaseCapture(); + } + } + + // Adjust the point from app client coordinates to webview client coordinates. + // WM_MOUSELEAVE messages don't have a point, so don't adjust the point. + if (message != WM_MOUSELEAVE) + { + point.x -= m_webViewBounds.left; + point.y -= m_webViewBounds.top; + } + + CHECK_FAILURE(m_compositionController->SendMouseInput( + static_cast(message), + static_cast(GET_KEYSTATE_WPARAM(wParam)), + mouseData, point)); + return true; + } + else if (message == WM_MOUSEMOVE && m_isTrackingMouse) + { + // When the mouse moves outside of the WebView, but still inside the app + // turn off mouse tracking and send the WebView a leave event. + m_isTrackingMouse = false; + TrackMouseEvents(TME_LEAVE | TME_CANCEL); + OnMouseMessage(WM_MOUSELEAVE, 0, 0); + } + } + return false; +} +//! [SendMouseInput] + +bool ViewComponent::OnPointerMessage(UINT message, WPARAM wParam, LPARAM lParam) +{ + bool handled = false; +#ifdef USE_WEBVIEW2_WIN10 + if (m_dcompDevice || m_wincompCompositor) +#else + if (m_dcompDevice) +#endif + { + POINT point; + POINTSTOPOINT(point, lParam); + UINT pointerId = GET_POINTERID_WPARAM(wParam); + + ::ScreenToClient(m_appWindow->GetMainWindow(), &point); + + bool pointerStartedInWebView = m_pointerIdsStartingInWebView.find(pointerId) != + m_pointerIdsStartingInWebView.end(); + // We want to send pointer input to the WebView for all pointers that either is in the + // WebView or started inside the WebView. For example, if a user started a page scroll + // inside of the WebView but dragged their finger outside of the WebView, we need to + // keep sending pointer events for those pointers. + if (PtInRect(&m_webViewBounds, point) || pointerStartedInWebView) + { + if (!pointerStartedInWebView && + (message == WM_POINTERENTER || message == WM_POINTERDOWN)) + { + m_pointerIdsStartingInWebView.insert(pointerId); + } + else if (message == WM_POINTERLEAVE) + { + m_pointerIdsStartingInWebView.erase(pointerId); + } + + handled = true; + wil::com_ptr pointer_info; + COREWEBVIEW2_MATRIX_4X4* webviewMatrix = + reinterpret_cast(&m_webViewTransformMatrix); + CHECK_FAILURE(m_compositionController->CreateCoreWebView2PointerInfoFromPointerId( + pointerId, m_appWindow->GetMainWindow(), *webviewMatrix, &pointer_info)); + CHECK_FAILURE(m_compositionController->SendPointerInput( + static_cast(message), pointer_info.get())); + } + } + return handled; +} + +void ViewComponent::TrackMouseEvents(DWORD mouseTrackingFlags) +{ + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(tme); + tme.dwFlags = mouseTrackingFlags; + tme.hwndTrack = m_appWindow->GetMainWindow(); + tme.dwHoverTime = 0; + ::TrackMouseEvent(&tme); +} + +//! [BuildDCompTree] +// Create host app visual that the WebView will connect to. +// - Create a IDCompositionTarget for the host window +// - Create a visual and set that as the IDCompositionTarget's root +// - Create another visual and add that to the IDCompositionTarget's root. +// This visual will be the visual root for the WebView. +void ViewComponent::BuildDCompTreeUsingVisual() +{ + CHECK_FAILURE_BOOL(m_dcompDevice != nullptr); + + if (m_dcompWebViewVisual == nullptr) + { + CHECK_FAILURE(m_dcompDevice->CreateTargetForHwnd( + m_appWindow->GetMainWindow(), TRUE, &m_dcompHwndTarget)); + CHECK_FAILURE(m_dcompDevice->CreateVisual(&m_dcompRootVisual)); + CHECK_FAILURE(m_dcompHwndTarget->SetRoot(m_dcompRootVisual.get())); + CHECK_FAILURE(m_dcompDevice->CreateVisual(&m_dcompWebViewVisual)); + CHECK_FAILURE(m_dcompRootVisual->AddVisual(m_dcompWebViewVisual.get(), TRUE, nullptr)); + } +} +//! [BuildDCompTree] + +void ViewComponent::DestroyDCompVisualTree() +{ + if (m_dcompWebViewVisual) + { + m_dcompWebViewVisual->RemoveAllVisuals(); + m_dcompWebViewVisual.reset(); + + m_dcompRootVisual->RemoveAllVisuals(); + m_dcompRootVisual.reset(); + + m_dcompHwndTarget->SetRoot(nullptr); + m_dcompHwndTarget.reset(); + + m_dcompDevice->Commit(); + } + + if (m_dcompTarget) + { + m_dcompTarget->RemoveOwnerRef(); + m_dcompTarget = nullptr; + } +} + +#ifdef USE_WEBVIEW2_WIN10 +void ViewComponent::BuildWinCompVisualTree() +{ + namespace abiComp = ABI::Windows::UI::Composition; + + if (m_wincompWebViewVisual == nullptr) + { + auto interop = m_wincompCompositor.as(); + winrt::check_hresult(interop->CreateDesktopWindowTarget( + m_appWindow->GetMainWindow(), false, + reinterpret_cast(winrt::put_abi(m_wincompHwndTarget)))); + + m_wincompRootVisual = m_wincompCompositor.CreateContainerVisual(); + m_wincompHwndTarget.Root(m_wincompRootVisual); + + m_wincompWebViewVisual = m_wincompCompositor.CreateContainerVisual(); + m_wincompRootVisual.Children().InsertAtTop(m_wincompWebViewVisual); + } +} + +void ViewComponent::DestroyWinCompVisualTree() +{ + if (m_wincompWebViewVisual != nullptr) + { + m_wincompWebViewVisual.Children().RemoveAll(); + m_wincompWebViewVisual = nullptr; + + m_wincompRootVisual.Children().RemoveAll(); + m_wincompRootVisual = nullptr; + + m_wincompHwndTarget.Root(nullptr); + m_wincompHwndTarget = nullptr; + } +} +#endif + +ViewComponent::~ViewComponent() +{ + m_controller->remove_ZoomFactorChanged(m_zoomFactorChangedToken); + if (m_compositionController) + { + m_compositionController->remove_CursorChanged(m_cursorChangedToken); + // If the webview closes because the AppWindow is closed (as opposed to being closed + // explicitly), this will no-op because in this case, the webview closes before the ViewComponent + // is destroyed. If the webview is closed explicitly, this will succeed. + m_compositionController->put_RootVisualTarget(nullptr); + DestroyDCompVisualTree(); +#ifdef USE_WEBVIEW2_WIN10 + DestroyWinCompVisualTree(); +#endif + } +} diff --git a/SampleApps/WebView2APISample/ViewComponent.h b/SampleApps/WebView2APISample/ViewComponent.h index 1703ca20..2c4acd75 100644 --- a/SampleApps/WebView2APISample/ViewComponent.h +++ b/SampleApps/WebView2APISample/ViewComponent.h @@ -1,108 +1,109 @@ -// Copyright (C) Microsoft Corporation. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include "stdafx.h" - -#include "AppWindow.h" -#include "ComponentBase.h" -#include -#include -#ifdef USE_WEBVIEW2_WIN10 -#include -#endif - -// This component handles commands from the View menu, as well as the ZoomFactorChanged -// event, and any functionality related to sizing and visibility of the WebView. -// It also manages interaction with the compositor if running in windowless mode. - -class DCompTargetImpl; - -class ViewComponent : public ComponentBase -{ - friend class DCompTargetImpl; - -public: - ViewComponent( - AppWindow* appWindow, - IDCompositionDevice* dcompDevice, -#ifdef USE_WEBVIEW2_WIN10 - winrtComp::Compositor wincompCompositor, -#endif - bool isDCompTargetMode - ); - - bool HandleWindowMessage( - HWND hWnd, - UINT message, - WPARAM wParam, - LPARAM lParam, - LRESULT* result) override; - - void SetBounds(RECT bounds); - - ~ViewComponent() override; - -private: - enum class TransformType - { - kIdentity = 0, - kScale2X, - kRotate30Deg, - kRotate60DegDiagonally - }; - void ResizeWebView(); - void ToggleVisibility(); - void SetSizeRatio(float ratio); - void SetZoomFactor(float zoom); - void SetScale(float scale); - void SetTransform(TransformType transformType); - void ShowWebViewBounds(); - void ShowWebViewZoom(); - AppWindow* m_appWindow = nullptr; - wil::com_ptr m_controller; - wil::com_ptr m_webView; - bool m_isDcompTargetMode; - bool m_isVisible = true; - float m_webViewRatio = 1.0f; - float m_webViewZoomFactor = 1.0f; - RECT m_webViewBounds = {}; - float m_webViewScale = 1.0f; - EventRegistrationToken m_zoomFactorChangedToken = {}; - - bool OnMouseMessage(UINT message, WPARAM wParam, LPARAM lParam); - bool OnPointerMessage(UINT message, WPARAM wParam, LPARAM lParam); - void TrackMouseEvents(DWORD mouseTrackingFlags); - - wil::com_ptr m_compositionController; - bool m_isTrackingMouse = false; - bool m_isCapturingMouse = false; - std::unordered_set m_pointerIdsStartingInWebView; - D2D1_MATRIX_4X4_F m_webViewTransformMatrix = D2D1::Matrix4x4F(); - - void BuildDCompTreeUsingVisual(); - void DestroyDCompVisualTree(); - - wil::com_ptr m_dcompDevice; - wil::com_ptr m_dcompHwndTarget; - wil::com_ptr m_dcompRootVisual; - wil::com_ptr m_dcompWebViewVisual; - -#ifdef USE_WEBVIEW2_WIN10 - void BuildWinCompVisualTree(); - void DestroyWinCompVisualTree(); - - winrt::Windows::UI::Composition::Compositor m_wincompCompositor{ nullptr }; - winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_wincompHwndTarget{ nullptr }; - winrt::Windows::UI::Composition::ContainerVisual m_wincompRootVisual{ nullptr }; - winrt::Windows::UI::Composition::ContainerVisual m_wincompWebViewVisual{ nullptr }; -#endif - - // This member is used to exercise the put_RootVisualTarget API with an IDCompositionTarget. - // Distinct/unrelated to the dcompHwndTarget - wil::com_ptr m_dcompTarget; - - EventRegistrationToken m_cursorChangedToken = {}; -}; +// Copyright (C) Microsoft Corporation. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include "stdafx.h" + +#include "AppWindow.h" +#include "ComponentBase.h" +#include +#include +#ifdef USE_WEBVIEW2_WIN10 +#include +#endif + +// This component handles commands from the View menu, as well as the ZoomFactorChanged +// event, and any functionality related to sizing and visibility of the WebView. +// It also manages interaction with the compositor if running in windowless mode. + +class DCompTargetImpl; + +class ViewComponent : public ComponentBase +{ + friend class DCompTargetImpl; + +public: + ViewComponent( + AppWindow* appWindow, + IDCompositionDevice* dcompDevice, +#ifdef USE_WEBVIEW2_WIN10 + winrtComp::Compositor wincompCompositor, +#endif + bool isDCompTargetMode + ); + + bool HandleWindowMessage( + HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam, + LRESULT* result) override; + + void SetBounds(RECT bounds); + + ~ViewComponent() override; + +private: + enum class TransformType + { + kIdentity = 0, + kScale2X, + kRotate30Deg, + kRotate60DegDiagonally + }; + void ResizeWebView(); + void ToggleVisibility(); + void SetSizeRatio(float ratio); + void SetZoomFactor(float zoom); + void SetScale(float scale); + void SetTransform(TransformType transformType); + void ShowWebViewBounds(); + void ShowWebViewZoom(); + AppWindow* m_appWindow = nullptr; + wil::com_ptr m_controller; + wil::com_ptr m_webView; + bool m_isDcompTargetMode; + bool m_isVisible = true; + float m_webViewRatio = 1.0f; + float m_webViewZoomFactor = 1.0f; + RECT m_webViewBounds = {}; + float m_webViewScale = 1.0f; + bool m_useCursorId = false; + EventRegistrationToken m_zoomFactorChangedToken = {}; + + bool OnMouseMessage(UINT message, WPARAM wParam, LPARAM lParam); + bool OnPointerMessage(UINT message, WPARAM wParam, LPARAM lParam); + void TrackMouseEvents(DWORD mouseTrackingFlags); + + wil::com_ptr m_compositionController; + bool m_isTrackingMouse = false; + bool m_isCapturingMouse = false; + std::unordered_set m_pointerIdsStartingInWebView; + D2D1_MATRIX_4X4_F m_webViewTransformMatrix = D2D1::Matrix4x4F(); + + void BuildDCompTreeUsingVisual(); + void DestroyDCompVisualTree(); + + wil::com_ptr m_dcompDevice; + wil::com_ptr m_dcompHwndTarget; + wil::com_ptr m_dcompRootVisual; + wil::com_ptr m_dcompWebViewVisual; + +#ifdef USE_WEBVIEW2_WIN10 + void BuildWinCompVisualTree(); + void DestroyWinCompVisualTree(); + + winrt::Windows::UI::Composition::Compositor m_wincompCompositor{ nullptr }; + winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_wincompHwndTarget{ nullptr }; + winrt::Windows::UI::Composition::ContainerVisual m_wincompRootVisual{ nullptr }; + winrt::Windows::UI::Composition::ContainerVisual m_wincompWebViewVisual{ nullptr }; +#endif + + // This member is used to exercise the put_RootVisualTarget API with an IDCompositionTarget. + // Distinct/unrelated to the dcompHwndTarget + wil::com_ptr m_dcompTarget; + + EventRegistrationToken m_cursorChangedToken = {}; +}; diff --git a/SampleApps/WebView2APISample/WebView2APISample.rc b/SampleApps/WebView2APISample/WebView2APISample.rc index 3567fbb8..0cdb3f80 100644 --- a/SampleApps/WebView2APISample/WebView2APISample.rc +++ b/SampleApps/WebView2APISample/WebView2APISample.rc @@ -1,296 +1,300 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#ifndef APSTUDIO_INVOKED -#include "targetver.h" -#endif -#define APSTUDIO_HIDDEN_SYMBOLS -#include "windows.h" -#undef APSTUDIO_HIDDEN_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_WEBVIEW2APISAMPLE ICON "WebView2APISample.ico" - -IDI_SMALL ICON "small.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDC_WEBVIEW2APISAMPLE MENU -BEGIN - POPUP "&File" - BEGIN - MENUITEM "Save Screenshot", IDM_SAVE_SCREENSHOT - MENUITEM "Get Document Title", IDM_GET_DOCUMENT_TITLE - MENUITEM "Get Browser Version After Creation", IDM_GET_BROWSER_VERSION_AFTER_CREATION - MENUITEM "Get Browser Version Before Creation", IDM_GET_BROWSER_VERSION_BEFORE_CREATION - MENUITEM "E&xit", IDM_EXIT - END - POPUP "&Script" - BEGIN - MENUITEM "Inject Script", IDM_INJECT_SCRIPT - MENUITEM "Add Initialize Script", ID_ADD_INITIALIZE_SCRIPT - MENUITEM "Remove Initialize Script", ID_REMOVE_INITIALIZE_SCRIPT - MENUITEM SEPARATOR - MENUITEM "Post Message String", IDM_POST_WEB_MESSAGE_STRING - MENUITEM "Post Message JSON", IDM_POST_WEB_MESSAGE_JSON - MENUITEM SEPARATOR - MENUITEM "Subscribe to CDP event", IDM_SUBSCRIBE_TO_CDP_EVENT - MENUITEM "Call CDP method", IDM_CALL_CDP_METHOD - MENUITEM SEPARATOR - MENUITEM "Add COM object", IDM_ADD_HOST_OBJECT - MENUITEM SEPARATOR - MENUITEM "Open DevTools Window", IDM_OPEN_DEVTOOLS_WINDOW - END - POPUP "&Window" - BEGIN - MENUITEM "Set WebView Language", IDM_SET_LANGUAGE - MENUITEM "Toggle AAD SSO enabled", IDM_TOGGLE_AAD_SSO - MENUITEM "Close WebView", IDM_CLOSE_WEBVIEW - MENUITEM "Close WebView and cleanup user data folder", IDM_CLOSE_WEBVIEW_CLEANUP - MENUITEM SEPARATOR - POPUP "WebView Creation Mode" - BEGIN - MENUITEM "Windowed", IDM_CREATION_MODE_WINDOWED - MENUITEM "Visual - DComp", IDM_CREATION_MODE_VISUAL_DCOMP - MENUITEM "Target - DComp", IDM_CREATION_MODE_TARGET_DCOMP -#ifdef USE_WEBVIEW2_WIN10 - MENUITEM "Visual - WinComp", IDM_CREATION_MODE_VISUAL_WINCOMP -#endif - END - MENUITEM "Create WebView" IDM_REINIT - MENUITEM "Create New Window", IDM_NEW_WINDOW - MENUITEM "Create New Thread", IDM_NEW_THREAD - END - POPUP "&Process" - BEGIN - MENUITEM "Browser Process Info", IDM_PROCESS_INFO - MENUITEM "Crash Browser Process", IDM_CRASH_PROCESS - MENUITEM "Crash Render Process", IDM_CRASH_RENDER_PROCESS - END - POPUP "S&ettings" - BEGIN - MENUITEM "Blocked Domains", ID_BLOCKEDSITES - MENUITEM "Set User Agent", ID_SETTINGS_SETUSERAGENT - MENUITEM SEPARATOR - MENUITEM "Toggle JavaScript", IDM_TOGGLE_JAVASCRIPT - MENUITEM "Toggle Web Messaging", IDM_TOGGLE_WEB_MESSAGING - MENUITEM "Toggle Fullscreen allowed", IDM_TOGGLE_FULLSCREEN_ALLOWED - MENUITEM "Toggle Status Bar enabled", ID_SETTINGS_STATUS_BAR_ENABLED - MENUITEM "Toggle DevTools enabled", ID_SETTINGS_DEV_TOOLS_ENABLED - MENUITEM "Toggle Block images", ID_SETTINGS_BLOCKALLIMAGES - POPUP "JavaScript Dialogs" - BEGIN - MENUITEM "Use Default Script Dialogs", IDM_USE_DEFAULT_SCRIPT_DIALOGS - MENUITEM "Use Custom Script Dialogs", IDM_USE_CUSTOM_SCRIPT_DIALOGS - MENUITEM "Use Deferred Script Dialogs", IDM_USE_DEFERRED_SCRIPT_DIALOGS - MENUITEM "Complete Deferred Script Dialog", IDM_COMPLETE_JAVASCRIPT_DIALOG - END - MENUITEM "Toggle context menus enabled", ID_SETTINGS_CONTEXT_MENUS_ENABLED - MENUITEM "Toggle host objects allowed", ID_SETTINGS_HOST_OBJECTS_ALLOWED - MENUITEM "Toggle zoom control enabled", ID_SETTINGS_ZOOM_ENABLED - MENUITEM "Toggle built-in error page enabled", ID_SETTINGS_BUILTIN_ERROR_PAGE_ENABLED - END - POPUP "&View" - BEGIN - MENUITEM "Toggle Visibility", IDM_TOGGLE_VISIBILITY - POPUP "WebView Area" - BEGIN - MENUITEM "Get WebView Bounds", IDM_GET_WEBVIEW_BOUNDS - MENUITEM "25%", IDM_SIZE_25 - MENUITEM "50%", IDM_SIZE_50 - MENUITEM "75%", IDM_SIZE_75 - MENUITEM "100%", IDM_SIZE_100 - END - POPUP "WebView Zoom" - BEGIN - MENUITEM "Get WebView Zoom", IDM_GET_WEBVIEW_ZOOM - MENUITEM "0.5x", IDM_ZOOM_05 - MENUITEM "1.0x", IDM_ZOOM_10 - MENUITEM "2.0x", IDM_ZOOM_20 - END - POPUP "WebView Scaling" - BEGIN - MENUITEM "Scale 0.5x" IDM_SCALE_50 - MENUITEM "Scale 1.0x" IDM_SCALE_100 - MENUITEM "Scale 1.25x" IDM_SCALE_125 - MENUITEM "Scale 1.5x" IDM_SCALE_150 - END - POPUP "WebView Transform" - BEGIN - MENUITEM "No transform" IDM_TRANSFORM_NONE - MENUITEM "Rotate 30Deg" IDM_TRANSFORM_ROTATE_30DEG - MENUITEM "Rotate 60Deg Diagonally" IDM_TRANSFORM_ROTATE_60DEG_DIAG - END - MENUITEM "Set Focus", IDM_FOCUS_SET - MENUITEM "Tab In", IDM_FOCUS_TAB_IN - MENUITEM "Reverse Tab In", IDM_FOCUS_REVERSE_TAB_IN - MENUITEM "Toggle Tab Handling", IDM_TOGGLE_TAB_HANDLING - END - POPUP "S&cenario" - BEGIN - MENUITEM "Web Messaging", IDM_SCENARIO_POST_WEB_MESSAGE - MENUITEM "Host Objects", IDM_SCENARIO_ADD_HOST_OBJECT - MENUITEM "Event Monitor", IDM_SCENARIO_WEB_VIEW_EVENT_MONITOR - POPUP "Script Debugging" - BEGIN - MENUITEM "JavaScript", IDM_SCENARIO_JAVA_SCRIPT - MENUITEM "TypeScript", IDM_SCENARIO_TYPE_SCRIPT - END - MENUITEM "Authentication", IDM_SCENARIO_AUTHENTICATION - END - POPUP "&Help" - BEGIN - MENUITEM "&About ...", IDM_ABOUT - END -END - -///////////////////////////////////////////////////////////////////////////// -// -// Accelerator -// - -IDC_WEBVIEW2APISAMPLE ACCELERATORS -BEGIN - "?", IDM_ABOUT, ASCII, ALT - "/", IDM_ABOUT, ASCII, ALT -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "About WebView2APISample" -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - ICON IDR_MAINFRAME,IDC_STATIC,14,14,21,20 - LTEXT "WebView2APISample, Version 1.0",IDC_STATIC,42,14,114,8,SS_NOPREFIX - LTEXT "Copyright (C) 2019",IDC_STATIC,42,26,114,8 - DEFPUSHBUTTON "OK",IDOK,113,41,50,14,WS_GROUP -END - -IDD_DIALOG_INPUT DIALOGEX 0, 0, 309, 151 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Input" -FONT 8, "MS Shell Dlg", 400, 0, 0x1 -BEGIN - GROUPBOX "Static",IDC_STATIC_LABEL,7,7,295,121 - EDITTEXT IDC_EDIT_INPUT,14,55,281,69,ES_MULTILINE | ES_AUTOHSCROLL - DEFPUSHBUTTON "OK",IDOK,198,130,50,14 - PUSHBUTTON "Cancel",IDCANCEL,252,130,50,14 - EDITTEXT IDC_EDIT_DESCRIPTION,14,18,281,33,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_ABOUTBOX, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 163 - TOPMARGIN, 7 - BOTTOMMARGIN, 55 - END - - IDD_DIALOG_INPUT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 302 - TOPMARGIN, 7 - BOTTOMMARGIN, 144 - END -END -#endif // APSTUDIO_INVOKED - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#ifndef APSTUDIO_INVOKED\r\n" - "#include ""targetver.h""\r\n" - "#endif\r\n" - "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" - "#include ""windows.h""\r\n" - "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDS_APP_TITLE "WebView2APISample" - IDC_WEBVIEW2APISAMPLE "WEBVIEW2APISAMPLE" -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef APSTUDIO_INVOKED +#include "targetver.h" +#endif +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_WEBVIEW2APISAMPLE ICON "WebView2APISample.ico" + +IDI_SMALL ICON "small.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_WEBVIEW2APISAMPLE MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "Save Screenshot", IDM_SAVE_SCREENSHOT + MENUITEM "Get Document Title", IDM_GET_DOCUMENT_TITLE + MENUITEM "Get Browser Version After Creation", IDM_GET_BROWSER_VERSION_AFTER_CREATION + MENUITEM "Get Browser Version Before Creation", IDM_GET_BROWSER_VERSION_BEFORE_CREATION + MENUITEM "E&xit", IDM_EXIT + END + POPUP "&Script" + BEGIN + MENUITEM "Inject Script", IDM_INJECT_SCRIPT + MENUITEM "Add Initialize Script", ID_ADD_INITIALIZE_SCRIPT + MENUITEM "Remove Initialize Script", ID_REMOVE_INITIALIZE_SCRIPT + MENUITEM SEPARATOR + MENUITEM "Post Message String", IDM_POST_WEB_MESSAGE_STRING + MENUITEM "Post Message JSON", IDM_POST_WEB_MESSAGE_JSON + MENUITEM SEPARATOR + MENUITEM "Subscribe to CDP event", IDM_SUBSCRIBE_TO_CDP_EVENT + MENUITEM "Call CDP method", IDM_CALL_CDP_METHOD + MENUITEM SEPARATOR + MENUITEM "Add COM object", IDM_ADD_HOST_OBJECT + MENUITEM SEPARATOR + MENUITEM "Open DevTools Window", IDM_OPEN_DEVTOOLS_WINDOW + END + POPUP "&Window" + BEGIN + MENUITEM "Set WebView Language", IDM_SET_LANGUAGE + MENUITEM "Toggle AAD SSO enabled", IDM_TOGGLE_AAD_SSO + MENUITEM "Close WebView", IDM_CLOSE_WEBVIEW + MENUITEM "Close WebView and cleanup user data folder", IDM_CLOSE_WEBVIEW_CLEANUP + MENUITEM SEPARATOR + POPUP "WebView Creation Mode" + BEGIN + MENUITEM "Windowed", IDM_CREATION_MODE_WINDOWED + MENUITEM "Visual - DComp", IDM_CREATION_MODE_VISUAL_DCOMP + MENUITEM "Target - DComp", IDM_CREATION_MODE_TARGET_DCOMP +#ifdef USE_WEBVIEW2_WIN10 + MENUITEM "Visual - WinComp", IDM_CREATION_MODE_VISUAL_WINCOMP +#endif + END + MENUITEM "Create WebView" IDM_REINIT + MENUITEM "Create New Window", IDM_NEW_WINDOW + MENUITEM "Create New Thread", IDM_NEW_THREAD + END + POPUP "&Process" + BEGIN + MENUITEM "Browser Process Info", IDM_PROCESS_INFO + MENUITEM "Crash Browser Process", IDM_CRASH_PROCESS + MENUITEM "Crash Render Process", IDM_CRASH_RENDER_PROCESS + END + POPUP "S&ettings" + BEGIN + MENUITEM "Blocked Domains", ID_BLOCKEDSITES + MENUITEM "Set User Agent", ID_SETTINGS_SETUSERAGENT + MENUITEM SEPARATOR + MENUITEM "Toggle JavaScript", IDM_TOGGLE_JAVASCRIPT + MENUITEM "Toggle Web Messaging", IDM_TOGGLE_WEB_MESSAGING + MENUITEM "Toggle Fullscreen allowed", IDM_TOGGLE_FULLSCREEN_ALLOWED + MENUITEM "Toggle Status Bar enabled", ID_SETTINGS_STATUS_BAR_ENABLED + MENUITEM "Toggle DevTools enabled", ID_SETTINGS_DEV_TOOLS_ENABLED + MENUITEM "Toggle Block images", ID_SETTINGS_BLOCKALLIMAGES + POPUP "JavaScript Dialogs" + BEGIN + MENUITEM "Use Default Script Dialogs", IDM_USE_DEFAULT_SCRIPT_DIALOGS + MENUITEM "Use Custom Script Dialogs", IDM_USE_CUSTOM_SCRIPT_DIALOGS + MENUITEM "Use Deferred Script Dialogs", IDM_USE_DEFERRED_SCRIPT_DIALOGS + MENUITEM "Complete Deferred Script Dialog", IDM_COMPLETE_JAVASCRIPT_DIALOG + END + MENUITEM "Toggle context menus enabled", ID_SETTINGS_CONTEXT_MENUS_ENABLED + MENUITEM "Toggle host objects allowed", ID_SETTINGS_HOST_OBJECTS_ALLOWED + MENUITEM "Toggle zoom control enabled", ID_SETTINGS_ZOOM_ENABLED + MENUITEM "Toggle built-in error page enabled", ID_SETTINGS_BUILTIN_ERROR_PAGE_ENABLED + END + POPUP "&View" + BEGIN + MENUITEM "Toggle Visibility", IDM_TOGGLE_VISIBILITY + POPUP "WebView Area" + BEGIN + MENUITEM "Get WebView Bounds", IDM_GET_WEBVIEW_BOUNDS + MENUITEM "25%", IDM_SIZE_25 + MENUITEM "50%", IDM_SIZE_50 + MENUITEM "75%", IDM_SIZE_75 + MENUITEM "100%", IDM_SIZE_100 + END + POPUP "WebView Zoom" + BEGIN + MENUITEM "Get WebView Zoom", IDM_GET_WEBVIEW_ZOOM + MENUITEM "0.5x", IDM_ZOOM_05 + MENUITEM "1.0x", IDM_ZOOM_10 + MENUITEM "2.0x", IDM_ZOOM_20 + END + POPUP "WebView Scaling" + BEGIN + MENUITEM "Scale 0.5x" IDM_SCALE_50 + MENUITEM "Scale 1.0x" IDM_SCALE_100 + MENUITEM "Scale 1.25x" IDM_SCALE_125 + MENUITEM "Scale 1.5x" IDM_SCALE_150 + END + POPUP "WebView Transform" + BEGIN + MENUITEM "No transform" IDM_TRANSFORM_NONE + MENUITEM "Rotate 30Deg" IDM_TRANSFORM_ROTATE_30DEG + MENUITEM "Rotate 60Deg Diagonally" IDM_TRANSFORM_ROTATE_60DEG_DIAG + END + MENUITEM "Toggle Cursor Handling" IDM_TOGGLE_CURSOR_HANDLING + MENUITEM "Set Focus", IDM_FOCUS_SET + MENUITEM "Tab In", IDM_FOCUS_TAB_IN + MENUITEM "Reverse Tab In", IDM_FOCUS_REVERSE_TAB_IN + MENUITEM "Toggle Tab Handling", IDM_TOGGLE_TAB_HANDLING + END + POPUP "S&cenario" + BEGIN + MENUITEM "Web Messaging", IDM_SCENARIO_POST_WEB_MESSAGE + MENUITEM "Host Objects", IDM_SCENARIO_ADD_HOST_OBJECT + MENUITEM "Event Monitor", IDM_SCENARIO_WEB_VIEW_EVENT_MONITOR + POPUP "Script Debugging" + BEGIN + MENUITEM "JavaScript", IDM_SCENARIO_JAVA_SCRIPT + MENUITEM "TypeScript", IDM_SCENARIO_TYPE_SCRIPT + END + MENUITEM "Authentication", IDM_SCENARIO_AUTHENTICATION + MENUITEM "Cookie Management", IDM_SCENARIO_COOKIE_MANAGEMENT + MENUITEM "DOM Content Loaded", IDM_SCENARIO_DOM_CONTENT_LOADED + MENUITEM "NavigateWithWebResourceRequest", IDM_SCENARIO_NAVIGATEWITHWEBRESOURCEREQUEST + END + POPUP "&Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_WEBVIEW2APISAMPLE ACCELERATORS +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOGEX 0, 0, 170, 62 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About WebView2APISample" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,14,14,21,20 + LTEXT "WebView2APISample, Version 1.0",IDC_STATIC,42,14,114,8,SS_NOPREFIX + LTEXT "Copyright (C) 2019",IDC_STATIC,42,26,114,8 + DEFPUSHBUTTON "OK",IDOK,113,41,50,14,WS_GROUP +END + +IDD_DIALOG_INPUT DIALOGEX 0, 0, 309, 151 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Input" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Static",IDC_STATIC_LABEL,7,7,295,121 + EDITTEXT IDC_EDIT_INPUT,14,55,281,69,ES_MULTILINE | ES_AUTOHSCROLL + DEFPUSHBUTTON "OK",IDOK,198,130,50,14 + PUSHBUTTON "Cancel",IDCANCEL,252,130,50,14 + EDITTEXT IDC_EDIT_DESCRIPTION,14,18,281,33,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 163 + TOPMARGIN, 7 + BOTTOMMARGIN, 55 + END + + IDD_DIALOG_INPUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 302 + TOPMARGIN, 7 + BOTTOMMARGIN, 144 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#ifndef APSTUDIO_INVOKED\r\n" + "#include ""targetver.h""\r\n" + "#endif\r\n" + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_APP_TITLE "WebView2APISample" + IDC_WEBVIEW2APISAMPLE "WEBVIEW2APISAMPLE" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/SampleApps/WebView2APISample/WebView2APISample.vcxproj b/SampleApps/WebView2APISample/WebView2APISample.vcxproj index 321ebc34..e625a505 100644 --- a/SampleApps/WebView2APISample/WebView2APISample.vcxproj +++ b/SampleApps/WebView2APISample/WebView2APISample.vcxproj @@ -1,475 +1,488 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - Debug - ARM64 - - - Release - ARM64 - - - Win7 Debug - ARM64 - - - Win7 Debug - Win32 - - - Win7 Debug - x64 - - - Win7 Release - ARM64 - - - Win7 Release - Win32 - - - Win7 Release - x64 - - - - 15.0 - {4F0CEEF3-12B3-425E-9BB0-105200411592} - Win32Proj - 10.0 - - - - Application - true - v142 - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - Unicode - - - Application - false - v142 - Unicode - - - Application - true - v142 - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - Unicode - - - Application - false - v142 - Unicode - - - Application - true - v142 - Unicode - - - Application - true - v142 - Unicode - - - Application - false - v142 - Unicode - - - Application - false - v142 - Unicode - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - $(ProjectDir)$(Configuration)\$(Platform)\ - $(Platform)\$(Configuration)\ - $(ProjectName) - - - true - $(ProjectDir)$(Configuration)\$(Platform)\ - $(Platform)\$(Configuration)\ - $(ProjectName)_Win7 - - - true - $(ProjectDir)$(Configuration)\$(Platform)\ - $(Platform)\$(Configuration)\ - - - true - $(ProjectDir)$(Configuration)\$(Platform)\ - $(Platform)\$(Configuration)\ - $(ProjectName)_Win7 - - - $(ProjectDir)$(Configuration)\$(Platform)\ - $(ProjectName) - - - $(ProjectDir)$(Configuration)\$(Platform)\ - $(ProjectName)_Win7 - - - $(ProjectDir)$(Configuration)\$(Platform)\ - - - $(ProjectDir)$(Configuration)\$(Platform)\ - $(ProjectName)_Win7 - - - $(ProjectDir)$(Configuration)\$(Platform)\ - $(ProjectName) - - - $(ProjectDir)$(Configuration)\$(Platform)\ - $(ProjectName)_Win7 - - - $(ProjectDir)$(Configuration)\$(Platform)\ - - - $(ProjectDir)$(Configuration)\$(Platform)\ - $(ProjectName)_Win7 - - - - USE_WEBVIEW2_WIN10;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - stdcpp17 - - - MachineX86 - true - Windows - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreadedDebug - Level3 - ProgramDatabase - Disabled - stdcpp17 - - - MachineX86 - true - Windows - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - USE_WEBVIEW2_WIN10;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - stdcpp17 - - - MachineX86 - true - Windows - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) - MultiThreaded - Level3 - ProgramDatabase - stdcpp17 - - - MachineX86 - true - Windows - true - true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreadedDebug - stdcpp17 - USE_WEBVIEW2_WIN10;_UNICODE;UNICODE;%(PreprocessorDefinitions) - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreadedDebug - stdcpp17 - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreaded - stdcpp17 - USE_WEBVIEW2_WIN10;_UNICODE;UNICODE;%(PreprocessorDefinitions) - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - - - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreaded - stdcpp17 - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - - - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreadedDebug - stdcpp17 - USE_WEBVIEW2_WIN10;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreadedDebug - stdcpp17 - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreaded - stdcpp17 - USE_WEBVIEW2_WIN10;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - MultiThreaded - stdcpp17 - - - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) - onecoreuap.lib %(AdditionalOptions) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - Create - Create - Create - Create - Create - Create - Create - Create - Create - Create - Create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + Win7 Debug + ARM64 + + + Win7 Debug + Win32 + + + Win7 Debug + x64 + + + Win7 Release + ARM64 + + + Win7 Release + Win32 + + + Win7 Release + x64 + + + + 15.0 + {4F0CEEF3-12B3-425E-9BB0-105200411592} + Win32Proj + 10.0 + + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + Unicode + + + Application + false + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + Unicode + + + Application + false + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + Unicode + + + Application + false + v142 + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + $(ProjectName) + + + true + $(ProjectDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + $(ProjectName)_Win7 + + + true + $(ProjectDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + + + true + $(ProjectDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + $(ProjectName)_Win7 + + + $(ProjectDir)$(Configuration)\$(Platform)\ + $(ProjectName) + + + $(ProjectDir)$(Configuration)\$(Platform)\ + $(ProjectName)_Win7 + + + $(ProjectDir)$(Configuration)\$(Platform)\ + + + $(ProjectDir)$(Configuration)\$(Platform)\ + $(ProjectName)_Win7 + + + $(ProjectDir)$(Configuration)\$(Platform)\ + $(ProjectName) + + + $(ProjectDir)$(Configuration)\$(Platform)\ + $(ProjectName)_Win7 + + + $(ProjectDir)$(Configuration)\$(Platform)\ + + + $(ProjectDir)$(Configuration)\$(Platform)\ + $(ProjectName)_Win7 + + + + USE_WEBVIEW2_WIN10;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + Level3 + ProgramDatabase + Disabled + stdcpp17 + + + MachineX86 + true + Windows + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDebug + Level3 + ProgramDatabase + Disabled + stdcpp17 + + + MachineX86 + true + Windows + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + USE_WEBVIEW2_WIN10;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + Level3 + ProgramDatabase + stdcpp17 + + + MachineX86 + true + Windows + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + Level3 + ProgramDatabase + stdcpp17 + + + MachineX86 + true + Windows + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreadedDebug + stdcpp17 + USE_WEBVIEW2_WIN10;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreadedDebug + stdcpp17 + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreaded + stdcpp17 + USE_WEBVIEW2_WIN10;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + + + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreaded + stdcpp17 + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + + + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreadedDebug + stdcpp17 + USE_WEBVIEW2_WIN10;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreadedDebug + stdcpp17 + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreaded + stdcpp17 + USE_WEBVIEW2_WIN10;_ARM64_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1;%(ClCompile.PreprocessorDefinitions) + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + MultiThreaded + stdcpp17 + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;shlwapi.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;urlmon.lib;%(AdditionalDependencies) + onecoreuap.lib %(AdditionalOptions) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/SampleApps/WebView2APISample/WebView2APISample.vcxproj.filters b/SampleApps/WebView2APISample/WebView2APISample.vcxproj.filters index d89ec716..495b2b34 100644 --- a/SampleApps/WebView2APISample/WebView2APISample.vcxproj.filters +++ b/SampleApps/WebView2APISample/WebView2APISample.vcxproj.filters @@ -72,6 +72,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -140,6 +152,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -150,13 +174,20 @@ + + + + + + Resource Files + diff --git a/SampleApps/WebView2APISample/documentation/Testing-Instructions.md b/SampleApps/WebView2APISample/documentation/Testing-Instructions.md index 763024d6..ffcd17a4 100644 --- a/SampleApps/WebView2APISample/documentation/Testing-Instructions.md +++ b/SampleApps/WebView2APISample/documentation/Testing-Instructions.md @@ -6,7 +6,7 @@ These are instructions for manually testing all the features of the WebView2 API * [Getting started](#Getting-started) * [Install a NuGet package Locally in VS](#Install-a-NuGet-package-Locally-in-VS) -* [UI Entries](#UI_Entries) +* [UI Entries](#ui-entries) * [File](#File) * [Save Screenshot](#Save-Screenshot) * [Get Document Title](#Get-Document-Title) @@ -17,11 +17,9 @@ These are instructions for manually testing all the features of the WebView2 API * [Inject Script](#Inject-Script) * [Post Message String](#Post-Message-String) * [Post Message JSON](#Post-Message-JSON) - * [Add Initialize Script](#Add/Remove-Initialize-Script) - * [Remove Initialize Script](#Add/Remove-Initialize-Script) - * [Subscribe to CDP event](#Subscribe-to-CDP-event-&-Call-CDP-method) - * [Call CDP method](#Subscribe-to-CDP-event-&-Call-CDP-method) - * [Add COM Object](#Add-COM-Object) + * [Add Initialize Script](#addremove-initialize-script) + * [Remove Initialize Script](#addremove-initialize-script) + * [Subscribe to CDP event & Call CDP method](#subscribe-to-cdp-event--call-cdp-method) * [Open DevTools Window](#Open-DevTools-Window) * [Window](#Window) * [Close WebView](#Close-WebView) @@ -60,7 +58,7 @@ These are instructions for manually testing all the features of the WebView2 API * [Host Objects](#Host-Objects) * [Script Debugging](#Script-Debugging) * [Help](#Help) - * [About ...](#About-...) + * [About ...](#about-) * [Miscellaneous](#Miscellaneous) * [Accelerator Key Support](#Accelerator-Key-Support) * [Language](#Language) @@ -723,7 +721,7 @@ Menu item `Script -> Host Objects` is demonstrated. Test Single WebView JavaScript Debugging with **both** [Debugger For Microsoft Edge](https://github.com/microsoft/vscode-edge-debug2) and [JavaScript Debugger Nightly](https://github.com/microsoft/vscode-js-debug) in VSCode -1. Follow [Debugging Setup](#[vscode]-debugging-setup) +1. Follow [Debugging Setup](#vscode-debugging-setup) 1. Go to Debug tab via `View -> Run` 1. On the top drop down, select `$(Debugger): Sample app (Script $(Configuration)|$(Platform))`. (e.g. `Debugger For Microsoft Edge: Sample app (Script Debug|x64)` and `JavaScript Debugger(Nightly): Sample app (Script Release|x64)`) ![debugger-dropdown](screenshots/debugger-dropdown.png) @@ -738,7 +736,7 @@ Test Single WebView JavaScript Debugging with **both** [Debugger For Microsoft E Test Single WebView TypeScript Debugging with **both** [Debugger For Microsoft Edge](https://github.com/microsoft/vscode-edge-debug2) and [JavaScript Debugger Nightly](https://github.com/microsoft/vscode-js-debug) in VSCode -1. Follow [Debugging Setup](#[vscode]-debugging-setup) +1. Follow [Debugging Setup](#vscode-debugging-setup) 1. Go to Debug tab via `View -> Run` 1. On the top drop down, select `$(Debugger): Sample app (Script $(Configuration)|$(Platform))`. (e.g. `Debugger For Microsoft Edge: Sample app (Script Debug|x64)` and `JavaScript Debugger(Nightly): Sample app (Script Release|x64)`) ![debugger-dropdown](screenshots/debugger-dropdown.png) @@ -755,7 +753,7 @@ Test Single WebView Script Debugging with **both** [Debugger For Microsoft Edge] 1. Add a new REGKEY `additionalBrowserArguments=--remote-debugging-port=9222` under `Computer\HKEY_CURRENT_USER\Software\Policies\Microsoft\EmbeddedBrowserWebView\LoaderOverride\*` ![step 1](screenshots/script-debugging-reg-key.png) -1. Follow [Debugging Setup](#[vscode]-debugging-setup) +1. Follow [Debugging Setup](#vscode-debugging-setup) 1. Go to Debug tab via `View -> Run` 1. On the top drop down, select `$(Debugger): Attach to Edge`. (e.g. `Debugger For Microsoft Edge: Attach to Edge` and `JavaScript Debugger(Nightly): Attach to Edge`) 1. Press `F5` or click the green Button (GO) to Start Debugging @@ -772,7 +770,7 @@ Test Single WebView Script Debugging with **both** [Debugger For Microsoft Edge] 1. Add a new REGKEY `additionalBrowserArguments=--remote-debugging-port=9222` under `Computer\HKEY_CURRENT_USER\Software\Policies\Microsoft\EmbeddedBrowserWebView\LoaderOverride\*` ![step 1](screenshots/script-debugging-reg-key.png) -1. Follow [Debugging Setup](#[vscode]-debugging-setup) +1. Follow [Debugging Setup](#vscode-debugging-setup) 1. Go to Debug tab via `View -> Run` 1. On the top drop down, select `$(Debugger): Attach to Edge`. (e.g. `Debugger For Microsoft Edge: Attach to Edge` and `JavaScript Debugger(Nightly): Attach to Edge`) 1. Press `F5` or click the green Button (GO) to Start Debugging diff --git a/SampleApps/WebView2APISample/packages.config b/SampleApps/WebView2APISample/packages.config index 3c8f0d64..413b11d1 100644 --- a/SampleApps/WebView2APISample/packages.config +++ b/SampleApps/WebView2APISample/packages.config @@ -1,5 +1,5 @@ - - - - + + + + \ No newline at end of file diff --git a/SampleApps/WebView2APISample/resource.h b/SampleApps/WebView2APISample/resource.h index 842ba9ea..5e1cd9d7 100644 --- a/SampleApps/WebView2APISample/resource.h +++ b/SampleApps/WebView2APISample/resource.h @@ -1,113 +1,118 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by WebView2APISample.rc -// -// clang-format off -#define IDC_MYICON 2 -#define IDS_APP_TITLE 102 -#define IDD_ABOUTBOX 103 -#define IDM_ABOUT 104 -#define IDM_EXIT 105 -#define IDI_WEBVIEW2APISAMPLE 107 -#define IDI_SMALL 108 -#define IDC_WEBVIEW2APISAMPLE 109 -#define IDC_WEBVIEW2APISAMPLEHOST 110 -#define IDM_ZOOM_05 111 -#define IDM_ZOOM_10 112 -#define IDM_ZOOM_20 113 -#define IDM_SAVE_SCREENSHOT 114 -#define IDM_TOGGLE_VISIBILITY 115 -#define IDM_CLOSE_WEBVIEW 116 -#define IDM_NEW_WINDOW 120 -#define IDM_PROCESS_INFO 121 -#define IDM_NEW_THREAD 122 -#define IDM_REINIT 123 -#define IDM_CRASH_PROCESS 125 -#define IDM_INJECT_SCRIPT 126 -#define IDM_GET_WEBVIEW_BOUNDS 127 -#define IDR_MAINFRAME 128 -#define IDM_POST_WEB_MESSAGE_STRING 130 -#define IDM_POST_WEB_MESSAGE_JSON 131 -#define IDM_TOGGLE_JAVASCRIPT 132 -#define IDM_TOGGLE_WEB_MESSAGING 133 -#define IDM_COMPLETE_JAVASCRIPT_DIALOG 137 -#define IDM_SUBSCRIBE_TO_CDP_EVENT 138 -#define IDM_CALL_CDP_METHOD 139 -#define IDM_TOGGLE_FULLSCREEN_ALLOWED 141 -#define IDD_DIALOG_INPUT 142 -#define IDM_FOCUS_SET 147 -#define IDM_FOCUS_TAB_IN 148 -#define IDM_FOCUS_REVERSE_TAB_IN 149 -#define IDM_SIZE_25 151 -#define IDM_SIZE_50 152 -#define IDM_SIZE_75 153 -#define IDM_SIZE_100 154 -#define IDM_TOGGLE_TAB_HANDLING 155 -#define ID_SETTINGS_STATUS_BAR_ENABLED 156 -#define ID_SETTINGS_DEV_TOOLS_ENABLED 157 -#define IDM_USE_DEFAULT_SCRIPT_DIALOGS 158 -#define IDM_USE_CUSTOM_SCRIPT_DIALOGS 159 -#define IDM_USE_DEFERRED_SCRIPT_DIALOGS 160 -#define IDM_GET_DOCUMENT_TITLE 161 -#define IDM_TRANSFORM_NONE 163 -#define IDM_TRANSFORM_ROTATE_30DEG 165 -#define IDM_TRANSFORM_ROTATE_60DEG_DIAG 166 -#define IDM_ADD_HOST_OBJECT 169 -#define IDM_GET_BROWSER_VERSION_AFTER_CREATION 170 -#define IDM_GET_BROWSER_VERSION_BEFORE_CREATION 171 -#define IDM_OPEN_DEVTOOLS_WINDOW 174 -#define IDM_CLOSE_WEBVIEW_CLEANUP 175 -#define IDM_SCALE_50 186 -#define IDM_SCALE_100 187 -#define IDM_SCALE_125 188 -#define IDM_SCALE_150 189 -#define IDM_GET_WEBVIEW_ZOOM 190 -#define IDM_CRASH_RENDER_PROCESS 191 -#define IDM_SET_LANGUAGE 192 -#define IDM_CREATION_MODE_WINDOWED 193 -#define IDM_CREATION_MODE_VISUAL_DCOMP 194 -#define IDM_CREATION_MODE_TARGET_DCOMP 195 -#ifdef USE_WEBVIEW2_WIN10 -#define IDM_CREATION_MODE_VISUAL_WINCOMP 196 -#endif -#define IDM_SCENARIO_JAVA_SCRIPT 199 -#define IDM_SCENARIO_TYPE_SCRIPT 200 -#define IDM_TOGGLE_AAD_SSO 201 - -#define IDE_ADDRESSBAR 1000 -#define IDE_ADDRESSBAR_GO 1001 -#define IDE_BACK 1002 -#define IDE_FORWARD 1003 -#define IDE_ADDRESSBAR_RELOAD 1004 -#define IDE_CANCEL 1005 -#define IDC_EDIT_INPUT 1006 -#define IDC_STATIC_LABEL 1007 -#define IDC_EDIT_DESCRIPTION 1008 -// Scenario IDMs -#define IDM_SCENARIO_AUTHENTICATION 2000 -#define IDM_SCENARIO_POST_WEB_MESSAGE 2001 -#define IDM_SCENARIO_WEB_VIEW_EVENT_MONITOR 2002 -#define IDM_SCENARIO_ADD_HOST_OBJECT 2003 -#define ID_BLOCKEDSITES 32773 -#define ID_SETTINGS_NAVIGATETOSTRING 32774 -#define ID_ADD_INITIALIZE_SCRIPT 32775 -#define ID_REMOVE_INITIALIZE_SCRIPT 32776 -#define ID_SETTINGS_BLOCKALLIMAGES 32777 -#define ID_SETTINGS_SETUSERAGENT 32778 -#define ID_SETTINGS_HOST_OBJECTS_ALLOWED 32779 -#define ID_SETTINGS_CONTEXT_MENUS_ENABLED 32780 -#define ID_SETTINGS_ZOOM_ENABLED 32781 -#define ID_SETTINGS_BUILTIN_ERROR_PAGE_ENABLED 32782 -#define IDC_STATIC -1 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NO_MFC 1 -#define _APS_NEXT_RESOURCE_VALUE 210 -#define _APS_NEXT_COMMAND_VALUE 32783 -#define _APS_NEXT_CONTROL_VALUE 1007 -#define _APS_NEXT_SYMED_VALUE 110 -#endif -#endif +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by WebView2APISample.rc +// +// clang-format off +#define IDC_MYICON 2 +#define IDS_APP_TITLE 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_WEBVIEW2APISAMPLE 107 +#define IDI_SMALL 108 +#define IDC_WEBVIEW2APISAMPLE 109 +#define IDC_WEBVIEW2APISAMPLEHOST 110 +#define IDM_ZOOM_05 111 +#define IDM_ZOOM_10 112 +#define IDM_ZOOM_20 113 +#define IDM_SAVE_SCREENSHOT 114 +#define IDM_TOGGLE_VISIBILITY 115 +#define IDM_CLOSE_WEBVIEW 116 +#define IDM_NEW_WINDOW 120 +#define IDM_PROCESS_INFO 121 +#define IDM_NEW_THREAD 122 +#define IDM_REINIT 123 +#define IDM_CRASH_PROCESS 125 +#define IDM_INJECT_SCRIPT 126 +#define IDM_GET_WEBVIEW_BOUNDS 127 +#define IDR_MAINFRAME 128 +#define IDM_POST_WEB_MESSAGE_STRING 130 +#define IDM_POST_WEB_MESSAGE_JSON 131 +#define IDM_TOGGLE_JAVASCRIPT 132 +#define IDM_TOGGLE_WEB_MESSAGING 133 +#define IDM_COMPLETE_JAVASCRIPT_DIALOG 137 +#define IDM_SUBSCRIBE_TO_CDP_EVENT 138 +#define IDM_CALL_CDP_METHOD 139 +#define IDM_TOGGLE_FULLSCREEN_ALLOWED 141 +#define IDD_DIALOG_INPUT 142 +#define IDM_FOCUS_SET 147 +#define IDM_FOCUS_TAB_IN 148 +#define IDM_FOCUS_REVERSE_TAB_IN 149 +#define IDM_SIZE_25 151 +#define IDM_SIZE_50 152 +#define IDM_SIZE_75 153 +#define IDM_SIZE_100 154 +#define IDM_TOGGLE_TAB_HANDLING 155 +#define ID_SETTINGS_STATUS_BAR_ENABLED 156 +#define ID_SETTINGS_DEV_TOOLS_ENABLED 157 +#define IDM_USE_DEFAULT_SCRIPT_DIALOGS 158 +#define IDM_USE_CUSTOM_SCRIPT_DIALOGS 159 +#define IDM_USE_DEFERRED_SCRIPT_DIALOGS 160 +#define IDM_GET_DOCUMENT_TITLE 161 +#define IDM_TRANSFORM_NONE 163 +#define IDM_TRANSFORM_ROTATE_30DEG 165 +#define IDM_TRANSFORM_ROTATE_60DEG_DIAG 166 +#define IDM_ADD_HOST_OBJECT 169 +#define IDM_GET_BROWSER_VERSION_AFTER_CREATION 170 +#define IDM_GET_BROWSER_VERSION_BEFORE_CREATION 171 +#define IDM_OPEN_DEVTOOLS_WINDOW 174 +#define IDM_CLOSE_WEBVIEW_CLEANUP 175 +#define IDM_SCALE_50 186 +#define IDM_SCALE_100 187 +#define IDM_SCALE_125 188 +#define IDM_SCALE_150 189 +#define IDM_GET_WEBVIEW_ZOOM 190 +#define IDM_CRASH_RENDER_PROCESS 191 +#define IDM_SET_LANGUAGE 192 +#define IDM_CREATION_MODE_WINDOWED 193 +#define IDM_CREATION_MODE_VISUAL_DCOMP 194 +#define IDM_CREATION_MODE_TARGET_DCOMP 195 +#ifdef USE_WEBVIEW2_WIN10 +#define IDM_CREATION_MODE_VISUAL_WINCOMP 196 +#endif +#define IDM_SCENARIO_JAVA_SCRIPT 199 +#define IDM_SCENARIO_TYPE_SCRIPT 200 +#define IDM_TOGGLE_AAD_SSO 201 +#define IDM_TOGGLE_CURSOR_HANDLING 208 + +#define IDE_ADDRESSBAR 1000 +#define IDE_ADDRESSBAR_GO 1001 +#define IDE_BACK 1002 +#define IDE_FORWARD 1003 +#define IDE_ADDRESSBAR_RELOAD 1004 +#define IDE_CANCEL 1005 +#define IDC_EDIT_INPUT 1006 +#define IDC_STATIC_LABEL 1007 +#define IDC_EDIT_DESCRIPTION 1008 +// Scenario IDMs +#define IDM_SCENARIO_COOKIE_MANAGEMENT 1999 +#define IDM_SCENARIO_AUTHENTICATION 2000 +#define IDM_SCENARIO_POST_WEB_MESSAGE 2001 +#define IDM_SCENARIO_WEB_VIEW_EVENT_MONITOR 2002 +#define IDM_SCENARIO_ADD_HOST_OBJECT 2003 +#define IDM_SCENARIO_DOM_CONTENT_LOADED 2004 +#define IDM_SCENARIO_NAVIGATEWITHWEBRESOURCEREQUEST 2005 + +#define ID_BLOCKEDSITES 32773 +#define ID_SETTINGS_NAVIGATETOSTRING 32774 +#define ID_ADD_INITIALIZE_SCRIPT 32775 +#define ID_REMOVE_INITIALIZE_SCRIPT 32776 +#define ID_SETTINGS_BLOCKALLIMAGES 32777 +#define ID_SETTINGS_SETUSERAGENT 32778 +#define ID_SETTINGS_HOST_OBJECTS_ALLOWED 32779 +#define ID_SETTINGS_CONTEXT_MENUS_ENABLED 32780 +#define ID_SETTINGS_ZOOM_ENABLED 32781 +#define ID_SETTINGS_BUILTIN_ERROR_PAGE_ENABLED 32782 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 210 +#define _APS_NEXT_COMMAND_VALUE 32783 +#define _APS_NEXT_CONTROL_VALUE 1007 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj b/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj index 907c05c8..6401a5a3 100644 --- a/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj +++ b/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj @@ -10,6 +10,6 @@ Debug;Release;Win7 Release;Win7 Debug - + \ No newline at end of file diff --git a/SampleApps/WebView2WpfBrowser/MainWindow.xaml b/SampleApps/WebView2WpfBrowser/MainWindow.xaml index 22273701..ccea6602 100644 --- a/SampleApps/WebView2WpfBrowser/MainWindow.xaml +++ b/SampleApps/WebView2WpfBrowser/MainWindow.xaml @@ -28,6 +28,11 @@ found in the LICENSE file. + + + + + @@ -40,6 +45,16 @@ found in the LICENSE file. + + + + + + + + + + diff --git a/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs b/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs index 6db19100..a4eb5e1f 100644 --- a/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs +++ b/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs @@ -3,9 +3,12 @@ // found in the LICENSE file. using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; +using System.Net; using System.Text; using System.Threading.Tasks; using System.Windows; @@ -18,6 +21,7 @@ using System.Windows.Navigation; using System.Windows.Shapes; using Microsoft.Web.WebView2.Core; +using Microsoft.Web.WebView2.Wpf; namespace WebView2WpfBrowser { @@ -27,6 +31,11 @@ namespace WebView2WpfBrowser public partial class MainWindow : Window { public static RoutedCommand InjectScriptCommand = new RoutedCommand(); + public static RoutedCommand NavigateWithWebResourceRequestCommand = new RoutedCommand(); + public static RoutedCommand DOMContentLoadedCommand = new RoutedCommand(); + public static RoutedCommand AddOrUpdateCookieCommand = new RoutedCommand(); + public static RoutedCommand DeleteCookiesCommand = new RoutedCommand(); + public static RoutedCommand DeleteAllCookiesCommand = new RoutedCommand(); bool _isNavigating = false; public MainWindow() @@ -131,12 +140,75 @@ async void InjectScriptCmdExecuted(object target, ExecutedRoutedEventArgs e) title: "Inject Script", description: "Enter some JavaScript to be executed in the context of this page.", defaultInput: "window.getComputedStyle(document.body).backgroundColor"); - if (dialog.ShowDialog() == true) { + if (dialog.ShowDialog() == true) + { string scriptResult = await webView.ExecuteScriptAsync(dialog.Input.Text); MessageBox.Show(this, scriptResult, "Script Result"); } } + void AddOrUpdateCookieCmdExecuted(object target, ExecutedRoutedEventArgs e) + { + CoreWebView2Cookie cookie = webView.CoreWebView2.CookieManager.CreateCookie("CookieName", "CookieValue", ".bing.com", "/"); + webView.CoreWebView2.CookieManager.AddOrUpdateCookie(cookie); + } + + void DeleteAllCookiesCmdExecuted(object target, ExecutedRoutedEventArgs e) + { + webView.CoreWebView2.CookieManager.DeleteAllCookies(); + } + + void DeleteCookiesCmdExecuted(object target, ExecutedRoutedEventArgs e) + { + webView.CoreWebView2.CookieManager.DeleteCookiesWithDomainAndPath("CookieName", ".bing.com", "/"); + } + + void DOMContentLoadedCmdExecuted(object target, ExecutedRoutedEventArgs e) + { + webView.CoreWebView2.DOMContentLoaded += (object sender, CoreWebView2DOMContentLoadedEventArgs arg) => + { + webView.ExecuteScriptAsync("let " + + "content=document.createElement(\"h2\");content.style.color=" + + "'blue';content.textContent= \"This text was added by the " + + "host app\";document.body.appendChild(content);"); + }; + webView.NavigateToString(@"

DOMContentLoaded sample page

The content below will be added after DOM content is loaded

"); + } + + private CoreWebView2Environment _coreWebView2Environment; + + async void NavigateWithWebResourceRequestCmdExecuted(object target, ExecutedRoutedEventArgs e) + { + // Need CoreWebView2Environment + if (_coreWebView2Environment == null) + { + _coreWebView2Environment = webView.CoreWebView2.Environment; + } + + // Prepare post data as UTF-8 byte array and convert it to stream + // as required by the application/x-www-form-urlencoded Content-Type + var dialog = new TextInputDialog( + title: "NavigateWithWebResourceRequest", + description: "Specify post data to submit to https://www.w3schools.com/action_page.php."); + if (dialog.ShowDialog() == true) + { + string postDataString = "input=" + dialog.Input.Text; + UTF8Encoding utfEncoding = new UTF8Encoding(); + byte[] postData = utfEncoding.GetBytes( + postDataString); + MemoryStream postDataStream = new MemoryStream(postDataString.Length); + postDataStream.Write(postData, 0, postData.Length); + postDataStream.Seek(0, SeekOrigin.Begin); + CoreWebView2WebResourceRequest webResourceRequest = + _coreWebView2Environment.CreateWebResourceRequest( + "https://www.w3schools.com/action_page.php", + "POST", + postDataStream, + "Content-Type: application/x-www-form-urlencoded\r\n"); + webView.CoreWebView2.NavigateWithWebResourceRequest(webResourceRequest); + } + } + void GoToPageCmdCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = webView != null && !_isNavigating; @@ -144,12 +216,11 @@ void GoToPageCmdCanExecute(object sender, CanExecuteRoutedEventArgs e) async void GoToPageCmdExecuted(object target, ExecutedRoutedEventArgs e) { - // Setting webView.Source will not trigger a navigation if the Source is the same - // as the previous Source. CoreWebView.Navigate() will always trigger a navigation. + // Setting webView.Source will not trigger a navigation if the Source is the same + // as the previous Source. CoreWebView.Navigate() will always trigger a navigation. await webView.EnsureCoreWebView2Async(); webView.CoreWebView2.Navigate((string)e.Parameter); } - void WebView_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e) { _isNavigating = true; @@ -162,6 +233,78 @@ void WebView_NavigationCompleted(object sender, CoreWebView2NavigationCompletedE RequeryCommands(); } + private static void OnShowNextWebResponseChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + MainWindow window = (MainWindow)d; + if ((bool)e.NewValue) + { + window.webView.CoreWebView2.WebResourceResponseReceived += window.CoreWebView2_WebResourceResponseReceived; + } + else + { + window.webView.CoreWebView2.WebResourceResponseReceived -= window.CoreWebView2_WebResourceResponseReceived; + } + } + + public static readonly DependencyProperty ShowNextWebResponseProperty = DependencyProperty.Register( + nameof(ShowNextWebResponse), + typeof(Boolean), + typeof(MainWindow), + new PropertyMetadata(false, OnShowNextWebResponseChanged)); + + public bool ShowNextWebResponse + { + get => (bool)this.GetValue(ShowNextWebResponseProperty); + set => this.SetValue(ShowNextWebResponseProperty, value); + } + + async void CoreWebView2_WebResourceResponseReceived(object sender, CoreWebView2WebResourceResponseReceivedEventArgs e) + { + ShowNextWebResponse = false; + + CoreWebView2WebResourceRequest request = e.Request; + CoreWebView2WebResourceResponseView response = e.Response; + + string caption = "Web Resource Response Received"; + // Start with capacity 64 for minimum message size + StringBuilder messageBuilder = new StringBuilder(64); + string HttpMessageContentToString(System.IO.Stream content) => content == null ? "[null]" : "[data]"; + void AppendHeaders(IEnumerable headers) + { + foreach (var header in headers) + { + messageBuilder.AppendLine($" {header}"); + } + } + + // Request + messageBuilder.AppendLine("Request"); + messageBuilder.AppendLine($"URI: {request.Uri}"); + messageBuilder.AppendLine($"Method: {request.Method}"); + messageBuilder.AppendLine("Headers:"); + AppendHeaders(request.Headers); + messageBuilder.AppendLine($"Content: {HttpMessageContentToString(request.Content)}"); + messageBuilder.AppendLine(); + + // Response + messageBuilder.AppendLine("Response"); + messageBuilder.AppendLine($"Status: {response.StatusCode}"); + messageBuilder.AppendLine($"Reason: {response.ReasonPhrase}"); + messageBuilder.AppendLine("Headers:"); + AppendHeaders(response.Headers); + try + { + Stream content = await response.GetContentAsync(); + messageBuilder.AppendLine($"Content: {HttpMessageContentToString(content)}"); + } + catch (System.Runtime.InteropServices.COMException) + { + messageBuilder.AppendLine($"Content: [failed to load]"); + } + + MessageBox.Show(messageBuilder.ToString(), caption); + } + void RequeryCommands() { // Seems like there should be a way to bind CanExecute directly to a bool property @@ -170,7 +313,7 @@ void RequeryCommands() // which signal that one of the underlying bool properties might have changed and // bluntly tell all commands to re-check their CanExecute status. // - // Another way to trigger this re-check would be to create our own bool dependency + // Another way to trigger this re-check would be to create our own bool dependency // properties on this class, bind them to the underlying properties, and implement a // PropertyChangedCallback on them. That arguably more directly binds the status of // the commands to the WebView's state, but at the cost of having an extraneous diff --git a/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj b/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj index e9acce7a..83554b51 100644 --- a/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj +++ b/SampleApps/WebView2WpfBrowser/WebView2WpfBrowser.csproj @@ -11,6 +11,6 @@ Debug;Release;Win7 Release;Win7 Debug - + \ No newline at end of file