diff --git a/SampleApps/WebView2APISample/AppWindow.cpp b/SampleApps/WebView2APISample/AppWindow.cpp index 1bb95c0f..1d85aebb 100644 --- a/SampleApps/WebView2APISample/AppWindow.cpp +++ b/SampleApps/WebView2APISample/AppWindow.cpp @@ -39,6 +39,8 @@ #include "ScenarioExtensionsManagement.h" #include "ScenarioIFrameDevicePermission.h" #include "ScenarioNavigateWithWebResourceRequest.h" +#include "ScenarioNonClientRegionSupport.h" +#include "ScenarioAcceleratorKeyPressed.h" #include "ScenarioNotificationReceived.h" #include "ScenarioPermissionManagement.h" #include "ScenarioSharedBuffer.h" @@ -182,9 +184,9 @@ AppWindow::AppWindow( UINT creationModeId, const WebViewCreateOption& opt, const std::wstring& initialUri, const std::wstring& userDataFolderParam, bool isMainWindow, std::function webviewCreatedCallback, bool customWindowRect, RECT windowRect, - bool shouldHaveToolbar) + bool shouldHaveToolbar, bool isPopup) : m_creationModeId(creationModeId), m_webviewOption(opt), m_initialUri(initialUri), - m_onWebViewFirstInitialized(webviewCreatedCallback) + m_onWebViewFirstInitialized(webviewCreatedCallback), m_isPopupWindow(isPopup) { // Initialize COM as STA. CHECK_FAILURE(OleInitialize(NULL)); @@ -651,6 +653,16 @@ bool AppWindow::ExecuteWebViewCommands(WPARAM wParam, LPARAM lParam) { return PrintToPdfStream(); } + case IDM_SCENARIO_NON_CLIENT_REGION_SUPPORT: + { + NewComponent(this); + return true; + } + case IDM_SCENARIO_ACCELERATOR_KEY_PRESSED: + { + NewComponent(this); + return true; + } } return false; } diff --git a/SampleApps/WebView2APISample/AppWindow.h b/SampleApps/WebView2APISample/AppWindow.h index ccbf488f..d37a1de1 100644 --- a/SampleApps/WebView2APISample/AppWindow.h +++ b/SampleApps/WebView2APISample/AppWindow.h @@ -87,15 +87,11 @@ class AppWindow { public: AppWindow( - UINT creationModeId, - const WebViewCreateOption& opt, - const std::wstring& initialUri = L"", - const std::wstring& userDataFolderParam = L"", - bool isMainWindow = false, - std::function webviewCreatedCallback = nullptr, - bool customWindowRect = false, - RECT windowRect = { 0 }, - bool shouldHaveToolbar = true); + UINT creationModeId, const WebViewCreateOption& opt, + const std::wstring& initialUri = L"", const std::wstring& userDataFolderParam = L"", + bool isMainWindow = false, std::function webviewCreatedCallback = nullptr, + bool customWindowRect = false, RECT windowRect = {0}, bool shouldHaveToolbar = true, + bool isPopup = false); ~AppWindow(); @@ -293,6 +289,7 @@ class AppWindow RECT m_appBackgroundImageRect; }; +// Creates and registers a component on this `AppWindow`. template void AppWindow::NewComponent(Args&&... args) { m_components.emplace_back(new ComponentType(std::forward(args)...)); diff --git a/SampleApps/WebView2APISample/CheckFailure.h b/SampleApps/WebView2APISample/CheckFailure.h index 763ceded..9eb953e8 100644 --- a/SampleApps/WebView2APISample/CheckFailure.h +++ b/SampleApps/WebView2APISample/CheckFailure.h @@ -35,3 +35,13 @@ void FeatureNotAvailable(); // Returns nothing, which is different from CHECK_FEATURE_RETURN #define CHECK_FEATURE_RETURN_EMPTY(feature) { if (!feature) { FeatureNotAvailable(); return; } } + +// Returns S_OK, which is different from CHECK_FEATURE_RETURN +#define CHECK_FEATURE_RETURN_HRESULT(feature) \ + { \ + if (!feature) \ + { \ + FeatureNotAvailable(); \ + return S_OK; \ + } \ + } diff --git a/SampleApps/WebView2APISample/ComponentBase.h b/SampleApps/WebView2APISample/ComponentBase.h index 1d23338e..aca1ce3d 100644 --- a/SampleApps/WebView2APISample/ComponentBase.h +++ b/SampleApps/WebView2APISample/ComponentBase.h @@ -6,6 +6,18 @@ #include "stdafx.h" +// A component is meant to encapsulate all details required for a specific +// capability of the AppWindow, typically demonstrating usage of a WebView2 API. +// +// Component instances are owned by an AppWindow, which will give each of its +// components a chance to handle any messages it gets. AppWindow deletes all its +// components when WebView is closed. +// +// Components are meant to be created and registered by AppWindow itself, +// through `AppWindow::NewComponent(...)`. For example, the +// AppWindow might create a new component upon selection of a menu item by the +// user. Components typically take and keep a pointer to their owning AppWindow +// so they can control the WebView. class ComponentBase { public: diff --git a/SampleApps/WebView2APISample/DiscardsComponent.cpp b/SampleApps/WebView2APISample/DiscardsComponent.cpp new file mode 100644 index 00000000..f97435da --- /dev/null +++ b/SampleApps/WebView2APISample/DiscardsComponent.cpp @@ -0,0 +1,23 @@ +// 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 "DiscardsComponent.h" + +DiscardsComponent::DiscardsComponent(AppWindow* appWindow) : m_appWindow(appWindow) +{ + m_appWindowDiscardsView = new AppWindow( + IDM_CREATION_MODE_WINDOWED, appWindow->GetWebViewOption(), L"edge://discards/graph", + appWindow->GetUserDataFolder(), false /* isMainWindow */, + nullptr /* webviewCreatedCallback */, true /* customWindowRect */, {100, 100, 900, 900}, + false /* shouldHaveToolbar */); + + m_appWindowDiscardsView->SetOnAppWindowClosing([&] { m_appWindow->DeleteComponent(this); }); +} + +DiscardsComponent::~DiscardsComponent() +{ + m_appWindowDiscardsView->SetOnAppWindowClosing(nullptr); +} diff --git a/SampleApps/WebView2APISample/DiscardsComponent.h b/SampleApps/WebView2APISample/DiscardsComponent.h new file mode 100644 index 00000000..cf9c52c1 --- /dev/null +++ b/SampleApps/WebView2APISample/DiscardsComponent.h @@ -0,0 +1,23 @@ +// 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" + +class DiscardsComponent : public ComponentBase +{ +public: + DiscardsComponent(AppWindow* appWindow); + ~DiscardsComponent() override; + +private: + AppWindow* m_appWindow = nullptr; + + // The AppWindow showing discards. + AppWindow* m_appWindowDiscardsView; +}; diff --git a/SampleApps/WebView2APISample/ProcessComponent.cpp b/SampleApps/WebView2APISample/ProcessComponent.cpp index 9919d1b1..72dda647 100644 --- a/SampleApps/WebView2APISample/ProcessComponent.cpp +++ b/SampleApps/WebView2APISample/ProcessComponent.cpp @@ -244,17 +244,17 @@ void ProcessComponent::AppendFrameInfo( CHECK_FAILURE(frameInfo->get_Source(&sourceRaw)); std::wstring source = sourceRaw.get()[0] ? sourceRaw.get() : L"none"; - wil::com_ptr frameInfoExperimental; - CHECK_FAILURE(frameInfo->QueryInterface(IID_PPV_ARGS(&frameInfoExperimental))); - frameInfoExperimental->get_FrameId(&frameId); - frameInfoExperimental->get_FrameKind(&frameKind); + wil::com_ptr frameInfo2; + CHECK_FAILURE(frameInfo->QueryInterface(IID_PPV_ARGS(&frameInfo2))); + frameInfo2->get_FrameId(&frameId); + frameInfo2->get_FrameKind(&frameKind); wil::com_ptr parentFrameInfo; - CHECK_FAILURE(frameInfoExperimental->get_ParentFrameInfo(&parentFrameInfo)); + CHECK_FAILURE(frameInfo2->get_ParentFrameInfo(&parentFrameInfo)); if (parentFrameInfo) { - CHECK_FAILURE(parentFrameInfo->QueryInterface(IID_PPV_ARGS(&frameInfoExperimental))); - CHECK_FAILURE(frameInfoExperimental->get_FrameId(&parentFrameId)); + CHECK_FAILURE(parentFrameInfo->QueryInterface(IID_PPV_ARGS(&frameInfo2))); + CHECK_FAILURE(frameInfo2->get_FrameId(&parentFrameId)); } wil::com_ptr mainFrameInfo = GetAncestorMainFrameInfo(frameInfo); @@ -262,8 +262,8 @@ void ProcessComponent::AppendFrameInfo( { type = L"main frame"; } - CHECK_FAILURE(mainFrameInfo->QueryInterface(IID_PPV_ARGS(&frameInfoExperimental))); - CHECK_FAILURE(frameInfoExperimental->get_FrameId(&mainFrameId)); + CHECK_FAILURE(mainFrameInfo->QueryInterface(IID_PPV_ARGS(&frameInfo2))); + CHECK_FAILURE(frameInfo2->get_FrameId(&mainFrameId)); wil::com_ptr childFrameInfo = GetAncestorMainFrameDirectChildFrameInfo(frameInfo); @@ -273,8 +273,8 @@ void ProcessComponent::AppendFrameInfo( } if (childFrameInfo) { - CHECK_FAILURE(childFrameInfo->QueryInterface(IID_PPV_ARGS(&frameInfoExperimental))); - CHECK_FAILURE(frameInfoExperimental->get_FrameId(&childFrameId)); + CHECK_FAILURE(childFrameInfo->QueryInterface(IID_PPV_ARGS(&frameInfo2))); + CHECK_FAILURE(frameInfo2->get_FrameId(&childFrameId)); } result << L"{frame name:" << name << L" | frame Id:" << frameId << L" | parent frame Id:" @@ -293,12 +293,12 @@ wil::com_ptr ProcessComponent::GetAncestorMainFrameInfo( wil::com_ptr frameInfo) { wil::com_ptr mainFrameInfo; - wil::com_ptr frameInfoExperimental; + wil::com_ptr frameInfo2; while (frameInfo) { mainFrameInfo = frameInfo; - CHECK_FAILURE(frameInfo->QueryInterface(IID_PPV_ARGS(&frameInfoExperimental))); - CHECK_FAILURE(frameInfoExperimental->get_ParentFrameInfo(&frameInfo)); + CHECK_FAILURE(frameInfo->QueryInterface(IID_PPV_ARGS(&frameInfo2))); + CHECK_FAILURE(frameInfo2->get_ParentFrameInfo(&frameInfo)); } return mainFrameInfo; } @@ -320,30 +320,28 @@ wil::com_ptr ProcessComponent::GetAncestorMainFrameDirec { wil::com_ptr mainFrameInfo; wil::com_ptr childFrameInfo; - wil::com_ptr frameInfoExperimental; + wil::com_ptr frameInfo2; while (frameInfo) { childFrameInfo = mainFrameInfo; mainFrameInfo = frameInfo; - CHECK_FAILURE(frameInfo->QueryInterface(IID_PPV_ARGS(&frameInfoExperimental))); - CHECK_FAILURE(frameInfoExperimental->get_ParentFrameInfo(&frameInfo)); + CHECK_FAILURE(frameInfo->QueryInterface(IID_PPV_ARGS(&frameInfo2))); + CHECK_FAILURE(frameInfo2->get_ParentFrameInfo(&frameInfo)); } return childFrameInfo; } void ProcessComponent::ShowProcessExtendedInfo() { - auto environmentExperimental13 = - m_webViewEnvironment.try_query(); - if (environmentExperimental13) + auto environment13 = m_webViewEnvironment.try_query(); + if (environment13) { //! [GetProcessExtendedInfos] - CHECK_FAILURE(environmentExperimental13->GetProcessExtendedInfos( - Callback( + CHECK_FAILURE(environment13->GetProcessExtendedInfos( + Callback( [this]( HRESULT error, - ICoreWebView2ExperimentalProcessExtendedInfoCollection* processCollection) - -> HRESULT + ICoreWebView2ProcessExtendedInfoCollection* processCollection) -> HRESULT { UINT32 processCount = 0; UINT32 rendererProcessCount = 0; @@ -352,7 +350,7 @@ void ProcessComponent::ShowProcessExtendedInfo() std::wstringstream rendererProcessInfos; for (UINT32 i = 0; i < processCount; i++) { - Microsoft::WRL::ComPtr + Microsoft::WRL::ComPtr processExtendedInfo; CHECK_FAILURE( processCollection->GetValueAtIndex(i, &processExtendedInfo)); diff --git a/SampleApps/WebView2APISample/ScenarioAcceleratorKeyPressed.cpp b/SampleApps/WebView2APISample/ScenarioAcceleratorKeyPressed.cpp new file mode 100644 index 00000000..dadaf811 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioAcceleratorKeyPressed.cpp @@ -0,0 +1,145 @@ +// 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 "ScenarioAcceleratorKeyPressed.h" + +#include "AppWindow.h" +#include "CheckFailure.h" + +using namespace Microsoft::WRL; + +static constexpr WCHAR c_samplePath[] = L"ScenarioAcceleratorKeyPressed.html"; +ScenarioAcceleratorKeyPressed::ScenarioAcceleratorKeyPressed(AppWindow* appWindow) + : m_appWindow(appWindow), m_controller(appWindow->GetWebViewController()), + m_webView(appWindow->GetWebView()) +{ + m_sampleUri = m_appWindow->GetLocalUri(c_samplePath); + wil::com_ptr settings; + CHECK_FAILURE(m_webView->get_Settings(&settings)); + m_settings3 = settings.try_query(); + // 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, 26, L"DisableBrowserAccelerators") == 0) + { + CHECK_FAILURE(m_settings3->put_AreBrowserAcceleratorKeysEnabled(FALSE)); + MessageBox( + nullptr, + L"Browser-specific accelerator keys, for example: \n" + L"Ctrl+F and F3 for Find on Page\n" + L"Ctrl+P for Print\n" + L"Ctrl+R and F5 for Reload\n" + L"Ctrl+Plus and Ctrl+Minus for zooming\n" + L"Ctrl+Shift-C and F12 for DevTools\n" + L"Special keys for browser functions, such as Back, Forward, and " + L"Search \n" + L"will be disabled after the next navigation except for F7.", + L"Settings change", MB_OK); + } + else if (message.compare(0, 25, L"EnableBrowserAccelerators") == 0) + { + CHECK_FAILURE(m_settings3->put_AreBrowserAcceleratorKeysEnabled(TRUE)); + MessageBox( + nullptr, + L"Browser-specific accelerator keys, for example: \n" + L"Ctrl+F and F3 for Find on Page\n" + L"Ctrl+R and F5 for Reload\n" + L"Ctrl+Plus and Ctrl+Minus for zooming\n" + L"Ctrl+Shift-C and F12 for DevTools\n" + L"Special keys for browser functions, such as Back, Forward, and " + L"Search \n" + L"will be enabled after the next navigation except for Ctr + P.", + L"Settings change", MB_OK); + } + return S_OK; + }) + .Get(), + &m_webMessageReceivedToken)); + + //! [IsBrowserAcceleratorKeyEnabled] + if (m_settings3) + { + // Register a handler for the AcceleratorKeyPressed event. + CHECK_FAILURE(m_controller->add_AcceleratorKeyPressed( + Callback( + [this]( + ICoreWebView2Controller* sender, + ICoreWebView2AcceleratorKeyPressedEventArgs* args) -> HRESULT + { + COREWEBVIEW2_KEY_EVENT_KIND kind; + CHECK_FAILURE(args->get_KeyEventKind(&kind)); + // We only care about key down events. + if (kind == COREWEBVIEW2_KEY_EVENT_KIND_KEY_DOWN || + kind == COREWEBVIEW2_KEY_EVENT_KIND_SYSTEM_KEY_DOWN) + { + UINT key; + CHECK_FAILURE(args->get_VirtualKey(&key)); + + wil::com_ptr args2; + + args->QueryInterface(IID_PPV_ARGS(&args2)); + if (args2) + { + if (key == 'P' && (GetKeyState(VK_CONTROL) < 0)) + { + // tell the browser to skip the key + CHECK_FAILURE(args2->put_IsBrowserAcceleratorKeyEnabled(FALSE)); + } + if (key == VK_F7) + { + // tell the browser to process the key + CHECK_FAILURE(args2->put_IsBrowserAcceleratorKeyEnabled(TRUE)); + } + } + } + return S_OK; + }) + .Get(), + &m_acceleratorKeyPressedToken)); + } + //! [IsBrowserAcceleratorKeyEnabled] + + // 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())); +} + +ScenarioAcceleratorKeyPressed::~ScenarioAcceleratorKeyPressed() +{ + m_webView->remove_WebMessageReceived(m_webMessageReceivedToken); + m_controller->remove_AcceleratorKeyPressed(m_acceleratorKeyPressedToken); + m_webView->remove_ContentLoading(m_contentLoadingToken); +} diff --git a/SampleApps/WebView2APISample/ScenarioAcceleratorKeyPressed.h b/SampleApps/WebView2APISample/ScenarioAcceleratorKeyPressed.h new file mode 100644 index 00000000..da287090 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioAcceleratorKeyPressed.h @@ -0,0 +1,29 @@ +// 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 ScenarioAcceleratorKeyPressed : public ComponentBase +{ +public: + ScenarioAcceleratorKeyPressed(AppWindow* appWindow); + ~ScenarioAcceleratorKeyPressed() override; + +private: + EventRegistrationToken m_acceleratorKeyPressedToken = {}; + EventRegistrationToken m_contentLoadingToken = {}; + EventRegistrationToken m_webMessageReceivedToken = {}; + + AppWindow* m_appWindow = nullptr; + std::wstring m_sampleUri; + wil::com_ptr m_controller; + wil::com_ptr m_webView; + wil::com_ptr m_settings3; +}; \ No newline at end of file diff --git a/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.cpp b/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.cpp index 868db399..1d884bf7 100644 --- a/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.cpp +++ b/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.cpp @@ -6,3 +6,111 @@ #include "AppWindow.h" #include "CheckFailure.h" #include "ScenarioNonClientRegionSupport.h" + +using namespace Microsoft::WRL; + +static constexpr WCHAR c_samplePath[] = L"ScenarioNonClientRegionSupport.html"; + +ScenarioNonClientRegionSupport::ScenarioNonClientRegionSupport(AppWindow* appWindow) + : m_appWindow(appWindow), m_webView(appWindow->GetWebView()) +{ + m_sampleUri = m_appWindow->GetLocalUri(c_samplePath); + + wil::com_ptr controller = appWindow->GetWebViewController(); + wil::com_ptr compController = + controller.try_query(); + if (compController) + { + m_compController5 = + compController.try_query(); + } + + CHECK_FAILURE(m_webView->get_Settings(&m_settings)); + + m_experimentalSettings = m_settings.try_query(); + + CHECK_FAILURE(m_webView->add_NavigationStarting( + Callback( + [this](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) + -> HRESULT + { + wil::unique_cotaskmem_string uri; + CHECK_FAILURE(args->get_Uri(&uri)); + CHECK_FEATURE_RETURN(m_experimentalSettings); + + BOOL enabled = 0; + CHECK_FAILURE( + m_experimentalSettings->get_IsNonClientRegionSupportEnabled(&enabled)); + + if (uri.get() == m_sampleUri && !enabled) + { + CHECK_FAILURE( + m_experimentalSettings->put_IsNonClientRegionSupportEnabled(TRUE)); + AddChangeListener(); + } + else if (uri.get() != m_sampleUri && enabled) + { + CHECK_FAILURE( + m_experimentalSettings->put_IsNonClientRegionSupportEnabled(FALSE)); + } + return S_OK; + }) + .Get(), + &m_navigationStartingToken)); + + // 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())); +} +//! [AddChangeListener] +void ScenarioNonClientRegionSupport::AddChangeListener() +{ + if (m_compController5) + { + CHECK_FAILURE(m_compController5->add_NonClientRegionChanged( + Callback( + [this]( + ICoreWebView2CompositionController* sender, + ICoreWebView2ExperimentalNonClientRegionChangedEventArgs* args) -> HRESULT + { + COREWEBVIEW2_NON_CLIENT_REGION_KIND region = + COREWEBVIEW2_NON_CLIENT_REGION_KIND_NOWHERE; + args->get_RegionKind(®ion); + wil::com_ptr + regionsCollection; + m_compController5->QueryNonClientRegion(region, ®ionsCollection); + UINT32 count = 0; + regionsCollection->get_Count(&count); + RECT rect; + regionsCollection->GetValueAtIndex(0, &rect); + return S_OK; + }) + .Get(), + &m_nonClientRegionChanged)); + } +} +//! [AddChangeListener] +ScenarioNonClientRegionSupport::~ScenarioNonClientRegionSupport() +{ + CHECK_FAILURE(m_webView->remove_NavigationStarting(m_navigationStartingToken)); + CHECK_FAILURE(m_webView->remove_ContentLoading(m_ContentLoadingToken)); + if (m_compController5) + { + CHECK_FAILURE( + m_compController5->remove_NonClientRegionChanged(m_nonClientRegionChanged)); + } +} \ No newline at end of file diff --git a/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.h b/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.h index 2a9fc1ab..368abe70 100644 --- a/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.h +++ b/SampleApps/WebView2APISample/ScenarioNonClientRegionSupport.h @@ -3,3 +3,29 @@ // found in the LICENSE file. #pragma once +#include "stdafx.h" + +#include + +#include "AppWindow.h" +#include "ComponentBase.h" + +class ScenarioNonClientRegionSupport : public ComponentBase +{ +public: + ScenarioNonClientRegionSupport(AppWindow* appWindow); + void AddChangeListener(); + ~ScenarioNonClientRegionSupport() override; + +private: + AppWindow* m_appWindow; + wil::com_ptr m_webView; + wil::com_ptr m_settings; + wil::com_ptr m_experimentalSettings; + wil::com_ptr m_compController5; + std::wstring m_sampleUri; + + EventRegistrationToken m_navigationStartingToken = {}; + EventRegistrationToken m_ContentLoadingToken = {}; + EventRegistrationToken m_nonClientRegionChanged = {}; +}; \ No newline at end of file diff --git a/SampleApps/WebView2APISample/ScenarioSaveAs.cpp b/SampleApps/WebView2APISample/ScenarioSaveAsStaging.cpp similarity index 91% rename from SampleApps/WebView2APISample/ScenarioSaveAs.cpp rename to SampleApps/WebView2APISample/ScenarioSaveAsStaging.cpp index 1634dff9..86472d2e 100644 --- a/SampleApps/WebView2APISample/ScenarioSaveAs.cpp +++ b/SampleApps/WebView2APISample/ScenarioSaveAsStaging.cpp @@ -4,7 +4,7 @@ #include "stdafx.h" -#include "ScenarioSaveAs.h" +#include "ScenarioSaveAsStaging.h" #include "App.h" #include "AppWindow.h" diff --git a/SampleApps/WebView2APISample/ScenarioSaveAs.h b/SampleApps/WebView2APISample/ScenarioSaveAsStaging.h similarity index 100% rename from SampleApps/WebView2APISample/ScenarioSaveAs.h rename to SampleApps/WebView2APISample/ScenarioSaveAsStaging.h diff --git a/SampleApps/WebView2APISample/ScenarioSharedWorkerWRR.cpp b/SampleApps/WebView2APISample/ScenarioSharedWorkerWRR.cpp index e0102fcc..02470aa1 100644 --- a/SampleApps/WebView2APISample/ScenarioSharedWorkerWRR.cpp +++ b/SampleApps/WebView2APISample/ScenarioSharedWorkerWRR.cpp @@ -16,8 +16,7 @@ ScenarioSharedWorkerWRR::ScenarioSharedWorkerWRR(AppWindow* appWindow) : m_webView(appWindow->GetWebView()) { //! [WebResourceRequested2] - wil::com_ptr webView = - m_webView.try_query(); + wil::com_ptr webView = m_webView.try_query(); if (webView) { // Filter must be added for application to receive any WebResourceRequested event @@ -28,7 +27,7 @@ ScenarioSharedWorkerWRR::ScenarioSharedWorkerWRR(AppWindow* appWindow) Callback( [this](ICoreWebView2* sender, ICoreWebView2WebResourceRequestedEventArgs* args) { - wil::com_ptr + wil::com_ptr webResourceRequestArgs; if (SUCCEEDED(args->QueryInterface(IID_PPV_ARGS(&webResourceRequestArgs)))) { @@ -65,14 +64,13 @@ ScenarioSharedWorkerWRR::ScenarioSharedWorkerWRR(AppWindow* appWindow) } //! [WebResourceRequested2] - CHECK_FAILURE( - m_webView->Navigate(L"https://mdn.github.io/simple-shared-worker/index2.html")); + CHECK_FAILURE(m_webView->Navigate( + L"https://mdn.github.io/dom-examples/web-workers/simple-shared-worker/index2.html")); } ScenarioSharedWorkerWRR::~ScenarioSharedWorkerWRR() { - wil::com_ptr webView = - m_webView.try_query(); + wil::com_ptr webView = m_webView.try_query(); if (webView) { CHECK_FAILURE(webView->RemoveWebResourceRequestedFilterWithRequestSourceKinds( diff --git a/SampleApps/WebView2APISample/ScenarioThrottlingControl.cpp b/SampleApps/WebView2APISample/ScenarioThrottlingControl.cpp new file mode 100644 index 00000000..d79cb3e6 --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioThrottlingControl.cpp @@ -0,0 +1,12 @@ +// 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 "ScenarioThrottlingControl.h" + +#include "CheckFailure.h" +#include "ScriptComponent.h" + +using namespace Microsoft::WRL; diff --git a/SampleApps/WebView2APISample/ScenarioThrottlingControl.h b/SampleApps/WebView2APISample/ScenarioThrottlingControl.h new file mode 100644 index 00000000..3368775a --- /dev/null +++ b/SampleApps/WebView2APISample/ScenarioThrottlingControl.h @@ -0,0 +1,10 @@ +// 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" diff --git a/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp b/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp index d9c26d24..099e44b2 100644 --- a/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp +++ b/SampleApps/WebView2APISample/ScenarioWebViewEventMonitor.cpp @@ -506,11 +506,10 @@ void ScenarioWebViewEventMonitor::EnableWebResourceRequestedEvent(bool enable) m_webviewEventSource->AddWebResourceRequestedFilter( L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL); - auto webViewExperimental16 = - m_webviewEventSource.try_query(); - if (webViewExperimental16) + auto webView2_22 = m_webviewEventSource.try_query(); + if (webView2_22) { - webViewExperimental16->AddWebResourceRequestedFilterWithRequestSourceKinds( + webView2_22->AddWebResourceRequestedFilterWithRequestSourceKinds( L"*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL, COREWEBVIEW2_WEB_RESOURCE_REQUEST_SOURCE_KINDS_ALL); } @@ -528,9 +527,9 @@ void ScenarioWebViewEventMonitor::EnableWebResourceRequestedEvent(bool enable) WebResourceRequestedToJsonString(webResourceRequest.get()); wil::com_ptr argsPtr = args; - wil::com_ptr - webResourceRequestArgs = argsPtr.try_query< - ICoreWebView2ExperimentalWebResourceRequestedEventArgs>(); + wil::com_ptr + webResourceRequestArgs = + argsPtr.try_query(); if (webResourceRequestArgs) { COREWEBVIEW2_WEB_RESOURCE_REQUEST_SOURCE_KINDS requestedSourceKind = @@ -993,21 +992,20 @@ void ScenarioWebViewEventMonitor::InitializeEventView(ICoreWebView2* webviewEven L"{ \"kind\": \"event\", \"name\": \"FrameCreated\", \"args\": {"; message += L"\"frame\": " + EncodeQuote(name.get()); - auto webView2Experimental23 = wil::com_ptr(sender) - .try_query(); - if (webView2Experimental23) + auto webView2_20 = + wil::com_ptr(sender).try_query(); + if (webView2_20) { UINT32 frameId = 0; - CHECK_FAILURE(webView2Experimental23->get_FrameId(&frameId)); + CHECK_FAILURE(webView2_20->get_FrameId(&frameId)); message += L",\"sender main frame id\": " + std::to_wstring((int)frameId); } - auto experimentalFrame5 = - webviewFrame.try_query(); - if (experimentalFrame5) + auto frame5 = webviewFrame.try_query(); + if (frame5) { UINT32 frameId = 0; - CHECK_FAILURE(experimentalFrame5->get_FrameId(&frameId)); + CHECK_FAILURE(frame5->get_FrameId(&frameId)); message += L",\"frame id\": " + std::to_wstring((int)frameId); } message += diff --git a/SampleApps/WebView2APISample/ScriptComponent.cpp b/SampleApps/WebView2APISample/ScriptComponent.cpp index 25823eb8..89ce9ee7 100644 --- a/SampleApps/WebView2APISample/ScriptComponent.cpp +++ b/SampleApps/WebView2APISample/ScriptComponent.cpp @@ -1003,10 +1003,9 @@ void ScriptComponent::ExecuteScriptWithResult() L"Enter the JavaScript code to run in the webview.", L""); if (dialog.confirmed) { - wil::com_ptr webview2 = - m_webView.try_query(); + wil::com_ptr webView = m_webView.try_query(); - if (!webview2) + if (!webView) { MessageBox( nullptr, L"Get webview2 failed!", L"ExecuteScriptWithResult Result", MB_OK); @@ -1016,13 +1015,12 @@ void ScriptComponent::ExecuteScriptWithResult() // The main interface for excute script, the first param is the string // which user want to execute, the second param is the callback to process // the result, here use a lamada to the param. - webview2->ExecuteScriptWithResult( + webView->ExecuteScriptWithResult( dialog.input.c_str(), - // The callback function has two param, the first one is the status of call. + // The callback function has two param, the first one is the status of call.s // it will always be the S_OK for now, and the second is the result struct. - Callback( - [this](HRESULT errorCode, ICoreWebView2ExperimentalExecuteScriptResult* result) - -> HRESULT + Callback( + [this](HRESULT errorCode, ICoreWebView2ExecuteScriptResult* result) -> HRESULT { if (errorCode != S_OK || result == nullptr) { @@ -1033,7 +1031,7 @@ void ScriptComponent::ExecuteScriptWithResult() } else { - wil::com_ptr exception; + wil::com_ptr exception; BOOL isSuccess; // User should always invoke the get_Success firstly to get if execute diff --git a/SampleApps/WebView2APISample/ScriptComponent.h b/SampleApps/WebView2APISample/ScriptComponent.h index da36df91..3319ff47 100644 --- a/SampleApps/WebView2APISample/ScriptComponent.h +++ b/SampleApps/WebView2APISample/ScriptComponent.h @@ -9,11 +9,14 @@ #include #include +#include #include #include "AppWindow.h" #include "ComponentBase.h" +const std::wstring GetJSONStringField(PCWSTR jsonMessage, PCWSTR fieldName); + // This component handles commands from the Script menu. class ScriptComponent : public ComponentBase { diff --git a/SampleApps/WebView2APISample/SettingsComponent.cpp b/SampleApps/WebView2APISample/SettingsComponent.cpp index e7f80003..4627bc70 100644 --- a/SampleApps/WebView2APISample/SettingsComponent.cpp +++ b/SampleApps/WebView2APISample/SettingsComponent.cpp @@ -1289,10 +1289,40 @@ bool SettingsComponent::HandleWindowMessage( case IDM_TRACKING_PREVENTION_LEVEL_STRICT: SetTrackingPreventionLevel(COREWEBVIEW2_TRACKING_PREVENTION_LEVEL_STRICT); return true; + case ID_SETTINGS_NON_CLIENT_REGION_SUPPORT_ENABLED: + { + //![ToggleNonClientRegionSupportEnabled] + BOOL nonClientRegionSupportEnabled; + wil::com_ptr experimentalSettings; + experimentalSettings = m_settings.try_query(); + CHECK_FEATURE_RETURN(experimentalSettings); + + CHECK_FAILURE(experimentalSettings->get_IsNonClientRegionSupportEnabled( + &nonClientRegionSupportEnabled)); + if (nonClientRegionSupportEnabled) + { + CHECK_FAILURE(experimentalSettings->put_IsNonClientRegionSupportEnabled(FALSE)); + MessageBox( + nullptr, + L"Non-client region support will be disabled after the next navigation", + L"Settings change", MB_OK); + } + else + { + CHECK_FAILURE(experimentalSettings->put_IsNonClientRegionSupportEnabled(TRUE)); + MessageBox( + nullptr, + L"Non-client region support will be enabled after the next navigation", + L"Settings change", MB_OK); + } + //! [ToggleNonClientRegionSupportEnabled] + return true; + } } } return false; } + void SettingsComponent::AddMenuItems( HMENU hPopupMenu, wil::com_ptr items) { diff --git a/SampleApps/WebView2APISample/ViewComponent.cpp b/SampleApps/WebView2APISample/ViewComponent.cpp index 901367c4..93f8eec9 100644 --- a/SampleApps/WebView2APISample/ViewComponent.cpp +++ b/SampleApps/WebView2APISample/ViewComponent.cpp @@ -173,6 +173,7 @@ ViewComponent::ViewComponent( } //! [SystemCursorId] } + if (SUCCEEDED(hr)) { SetClassLongPtr( @@ -208,6 +209,33 @@ ViewComponent::ViewComponent( bool ViewComponent::HandleWindowMessage( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result) { + wil::com_ptr compositionController5; + + if (m_compositionController) + { + compositionController5 = + m_compositionController + .try_query(); + } + + //! [DraggableRegions1] + if (message == WM_NCHITTEST && compositionController5) + { + POINT point{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + ScreenToClient(hWnd, &point); + if (PtInRect(&m_webViewBounds, point)) + { + point.x -= m_webViewBounds.left; + point.y -= m_webViewBounds.top; + + COREWEBVIEW2_NON_CLIENT_REGION_KIND region = + COREWEBVIEW2_NON_CLIENT_REGION_KIND_NOWHERE; + CHECK_FAILURE(compositionController5->GetNonClientRegionAtPoint(point, ®ion)); + *result = region; + return true; + } + } + //! [DraggableRegions1] if (message == WM_COMMAND) { switch (LOWORD(wParam)) @@ -357,6 +385,7 @@ bool ViewComponent::HandleWindowMessage( } //! [ToggleIsVisibleOnMinimize] if ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) + || (message == WM_NCRBUTTONUP || message == WM_NCRBUTTONDOWN) || message == WM_MOUSELEAVE) { return OnMouseMessage(message, wParam, lParam); @@ -377,6 +406,7 @@ bool ViewComponent::HandleWindowMessage( //! [NotifyParentWindowPositionChanged] return false; } + //! [SetPreferredColorScheme] void ViewComponent::SetPreferredColorScheme(COREWEBVIEW2_PREFERRED_COLOR_SCHEME value) { @@ -708,6 +738,9 @@ bool ViewComponent::OnMouseMessage(UINT message, WPARAM wParam, LPARAM lParam) POINTSTOPOINT(point, lParam); if (message == WM_MOUSEWHEEL || message == WM_MOUSEHWHEEL + //! [DraggableRegions2] + || message == WM_NCRBUTTONDOWN || message == WM_NCRBUTTONUP + //! [DraggableRegions2] ) { // Mouse wheel messages are delivered in screen coordinates. diff --git a/SampleApps/WebView2APISample/ViewComponent.h b/SampleApps/WebView2APISample/ViewComponent.h index 08945122..b2536fe8 100644 --- a/SampleApps/WebView2APISample/ViewComponent.h +++ b/SampleApps/WebView2APISample/ViewComponent.h @@ -111,6 +111,7 @@ class ViewComponent : public ComponentBase bool m_isCapturingMouse = false; std::unordered_set m_pointerIdsStartingInWebView; D2D1_MATRIX_4X4_F m_webViewTransformMatrix = D2D1::Matrix4x4F(); + void BuildDCompTreeUsingVisual(); void DestroyDCompVisualTree(); diff --git a/SampleApps/WebView2APISample/WebView2APISample.rc b/SampleApps/WebView2APISample/WebView2APISample.rc index dcd89070..c0078707 100644 --- a/SampleApps/WebView2APISample/WebView2APISample.rc +++ b/SampleApps/WebView2APISample/WebView2APISample.rc @@ -163,6 +163,7 @@ BEGIN MENUITEM "Balanced", IDM_TRACKING_PREVENTION_LEVEL_BALANCED MENUITEM "Strict", IDM_TRACKING_PREVENTION_LEVEL_STRICT END + MENUITEM "Toggle Non-Client Region Support", ID_SETTINGS_NON_CLIENT_REGION_SUPPORT_ENABLED END POPUP "&View" BEGIN @@ -286,12 +287,14 @@ BEGIN MENUITEM "JavaScript Virtual File", IDM_SCENARIO_JAVA_SCRIPT_VIRTUAL MENUITEM "TypeScript Virtual File", IDM_SCENARIO_TYPE_SCRIPT_VIRTUAL END + MENUITEM "Non-Client Region Support", IDM_SCENARIO_NON_CLIENT_REGION_SUPPORT + MENUITEM "Shared Buffer", IDM_SCENARIO_SHARED_BUFFER MENUITEM "Testing Focus", IDM_SCENARIO_TESTING_FOCUS MENUITEM "Virtual Host Mapping With Service Worker", IDM_SCENARIO_VIRTUAL_HOST_MAPPING MENUITEM "Virtual Host Mapping For Pop Up Window", IDM_SCENARIO_VIRTUAL_HOST_MAPPING_POP_UP_WINDOW MENUITEM "Web Messaging", IDM_SCENARIO_POST_WEB_MESSAGE MENUITEM "WebView Event Monitor", IDM_SCENARIO_WEB_VIEW_EVENT_MONITOR - MENUITEM "Shared Buffer", IDM_SCENARIO_SHARED_BUFFER + MENUITEM "Accelerator Key Pressed", IDM_SCENARIO_ACCELERATOR_KEY_PRESSED END POPUP "&Audio" BEGIN diff --git a/SampleApps/WebView2APISample/WebView2APISample.vcxproj b/SampleApps/WebView2APISample/WebView2APISample.vcxproj index dc4f6515..86aeb6bb 100644 --- a/SampleApps/WebView2APISample/WebView2APISample.vcxproj +++ b/SampleApps/WebView2APISample/WebView2APISample.vcxproj @@ -218,6 +218,7 @@ + @@ -225,6 +226,7 @@ + @@ -240,9 +242,10 @@ - + + @@ -266,12 +269,14 @@ + + @@ -287,9 +292,10 @@ - + + @@ -336,6 +342,9 @@ $(OutDir)\assets + + $(OutDir)\assets + $(OutDir)\assets @@ -363,6 +372,24 @@ $(OutDir)\assets + + $(OutDir)\assets + + + $(OutDir)\assets + + + $(OutDir)\assets + + + $(OutDir)\assets + + + $(OutDir)\assets + + + $(OutDir)\assets + $(OutDir)\assets @@ -425,13 +452,13 @@ - + 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}. - + diff --git a/SampleApps/WebView2APISample/assets/ScenarioAcceleratorKeyPressed.html b/SampleApps/WebView2APISample/assets/ScenarioAcceleratorKeyPressed.html new file mode 100644 index 00000000..1b848b88 --- /dev/null +++ b/SampleApps/WebView2APISample/assets/ScenarioAcceleratorKeyPressed.html @@ -0,0 +1,24 @@ + + + + ScenarioAcceleratorKeyPressed + + + + +

Disable all Browser Accelerator keys but F7 remains enabled

+ + + +

Enable all Browser Accelerator keys but Ctrl + P remains disabled

+ + + + \ No newline at end of file diff --git a/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl.html b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl.html new file mode 100644 index 00000000..70345bf2 --- /dev/null +++ b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl.html @@ -0,0 +1,15 @@ + + + Throttling Control + + + + +

main

+
+ + +
+ + + diff --git a/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl.js b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl.js new file mode 100644 index 00000000..1cdd50d4 --- /dev/null +++ b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl.js @@ -0,0 +1,75 @@ +// target number of iterations +const limitIterations = false; +const iterations = 20; + +// steps per iteration +const steps = 20; + +// global state +let iteration = 0; +let counter = 0; +let start = performance.now(); +let timerId = undefined; + +const frameId = document.querySelector('meta[name="frame-title"]').content; +const logger = (frameId == 'main') ? window.open('/ScenarioThrottlingControlMonitor.html') : window.parent; + +const timerCallback = () => { + let now = performance.now(); + counter++; + + // compute average timer delay only after target steps + if (counter == steps) { + let avg = (now - start) / steps; + onIterationCompleted(avg); + } +} + +function reportAverageDelay(delay) { + console.log(`[${frameId}] avg: ${delay} ms`); + let message = { + "frameId": frameId, + "delayAvg": delay + }; + logger.postMessage(message); +} + +function onIterationCompleted(delayAvg) { + start = performance.now(); + counter = 0; + + if (++iteration == iterations && limitIterations) { + clearInterval(timerId); + timerId = undefined; + } + + reportAverageDelay(delayAvg); +} + +document.addEventListener("visibilitychange", () => { + let message = { + "frameId": frameId, + "visibilityUpdate": document.visibilityState + }; + logger.postMessage(message); +}); + +window.addEventListener('message', (event) => { + console.log(`[${frameId}] got message: ${event.data}`); + + if (event.data == 'init') { + // init timer/state + counter = 0; + start = performance.now(); + timerId = setInterval(timerCallback, 0); + + // fwd to embedded frames + if (frameId == 'main') { + document.getElementById('trusted').contentWindow.postMessage(event.data); + document.getElementById('untrusted').contentWindow.postMessage(event.data); + } + } else if (frameId == 'main') { + // log from embedded frame, fwd to popup + logger.postMessage(event.data); + } +}); diff --git a/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl1PP.html b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl1PP.html new file mode 100644 index 00000000..029a30d9 --- /dev/null +++ b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl1PP.html @@ -0,0 +1,11 @@ + + + 1PP Frame + + + + +

trusted

+ + + diff --git a/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl3PP.html b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl3PP.html new file mode 100644 index 00000000..8b311bc8 --- /dev/null +++ b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControl3PP.html @@ -0,0 +1,11 @@ + + + 3PP Frame + + + + +

untrusted

+ + + diff --git a/SampleApps/WebView2APISample/assets/ScenarioThrottlingControlMonitor.html b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControlMonitor.html new file mode 100644 index 00000000..2a951275 --- /dev/null +++ b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControlMonitor.html @@ -0,0 +1,71 @@ + + + Throttling Control + + + +

Throttling Control

+ + +
+
+ page state: + foreground +
+ +
+ + +
+
+ + + +
+ +
+ + + +
+ +
+ + + +
+
+
+ +
+ Scenarios +
+ + +
+
+ + +
+
+
+ + +
+ + +
+
+
+ + +
+
+ + +
+
+ + + + diff --git a/SampleApps/WebView2APISample/assets/ScenarioThrottlingControlMonitor.js b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControlMonitor.js new file mode 100644 index 00000000..8d833d20 --- /dev/null +++ b/SampleApps/WebView2APISample/assets/ScenarioThrottlingControlMonitor.js @@ -0,0 +1,70 @@ +let counters = { + 'main': 0, + 'trusted': 0, + 'untrusted': 0 +}; + +function logLine (target, content) { + let output = document.getElementById(`output-${target}`); + let line = `${counters[`${target}`]++}`; + output.textContent += `${line.padStart(3, ' ')} | ${content}\n`; + + output.scrollTop = output.scrollHeight; +} + +window.addEventListener('message', (event) => { + console.log(event.data); + + let frameId = event.data.frameId; + + if (event.data.visibilityUpdate) { + let visibility = event.data.visibilityUpdate; + logLine(frameId, `[visibility: ${visibility}]`); + document.getElementById('page-state').textContent = visibility == 'hidden' ? 'background' : 'foreground'; + } else { + // reporting delay + let delayText = event.data.delayAvg.toFixed(2); + logLine(frameId, `${delayText} ms`); + } +}); + +function toggleVisibility() { + let message = { + command: 'toggle-visibility', + }; + + chrome.webview.postMessage(message); +} + +function setTimerInterval(priority) { + let interval = document.getElementById(`interval-${priority}`).value; + if (interval === '' || isNaN(interval)) { + console.log('invalid value'); + return; + } + + let message = { + command: 'set-interval', + params: { + priority: priority, + intervalMs: interval + } + }; + + console.log(message); + chrome.webview.postMessage(message); +} + +function triggerScenario(label) { + let message = { + command: 'scenario', + params: { + label: label + } + }; + + chrome.webview.postMessage(message); +} + +// notify target so it can start timers +window.opener.postMessage('init'); diff --git a/SampleApps/WebView2APISample/packages.config b/SampleApps/WebView2APISample/packages.config index c32cfbf7..6f082d4e 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 9da8d1cb..a444fd7b 100644 --- a/SampleApps/WebView2APISample/resource.h +++ b/SampleApps/WebView2APISample/resource.h @@ -175,7 +175,9 @@ #define IDM_SCENARIO_SHARED_BUFFER 2034 #define IDM_PERMISSION_MANAGEMENT 2036 #define IDM_SCENARIO_CLEAR_CUSTOM_DATA_PARTITION 2037 +#define IDM_SCENARIO_NON_CLIENT_REGION_SUPPORT 2038 #define IDM_SCENARIO_NOTIFICATION 2039 +#define IDM_SCENARIO_ACCELERATOR_KEY_PRESSED 2042 #define IDM_CREATION_MODE_WINDOWED 3000 #define IDM_CREATION_MODE_VISUAL_DCOMP 3001 #define IDM_CREATION_MODE_TARGET_DCOMP 3002 @@ -212,6 +214,7 @@ #define ID_TOGGLE_LAUNCHING_EXTERNAL_URI_SCHEME_ENABLED 32802 #define IDC_CHECK_USE_OS_REGION 32803 #define ID_CUSTOM_DATA_PARTITION 32804 +#define ID_SETTINGS_NON_CLIENT_REGION_SUPPORT_ENABLED 32805 #define IDC_STATIC -1 // Next default values for new objects // diff --git a/SampleApps/WebView2WindowsFormsBrowser/BrowserForm.Designer.cs b/SampleApps/WebView2WindowsFormsBrowser/BrowserForm.Designer.cs index b56843db..cf403323 100644 --- a/SampleApps/WebView2WindowsFormsBrowser/BrowserForm.Designer.cs +++ b/SampleApps/WebView2WindowsFormsBrowser/BrowserForm.Designer.cs @@ -338,6 +338,7 @@ private void InitializeComponent() this.acceleratorKeysEnabledToolStripMenuItem.Text = "Toggle AcceleratorKeys"; this.acceleratorKeysEnabledToolStripMenuItem.Checked = true; this.acceleratorKeysEnabledToolStripMenuItem.CheckOnClick = true; + this.acceleratorKeysEnabledToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; // // allowExternalDropMenuItem // @@ -347,6 +348,7 @@ private void InitializeComponent() this.allowExternalDropMenuItem.Checked = true; this.allowExternalDropMenuItem.CheckOnClick = true; this.allowExternalDropMenuItem.Click += new System.EventHandler(this.allowExternalDropMenuItem_Click); + this.allowExternalDropMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; // // serverCertificateErrorMenuItem // @@ -370,6 +372,7 @@ private void InitializeComponent() this.toggleCustomServerCertificateSupportMenuItem.Size = new System.Drawing.Size(359, 44); this.toggleCustomServerCertificateSupportMenuItem.Text = "Toggle Custom Server Certificate Support"; this.toggleCustomServerCertificateSupportMenuItem.Click += new System.EventHandler(this.toggleCustomServerCertificateSupportMenuItem_Click); + this.toggleCustomServerCertificateSupportMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; // // clearServerCertificateErrorActionsMenuItem // @@ -384,6 +387,7 @@ private void InitializeComponent() this.toggleDefaultScriptDialogsMenuItem.Size = new System.Drawing.Size(359, 44); this.toggleDefaultScriptDialogsMenuItem.Text = "Toggle Default Script Dialogs"; this.toggleDefaultScriptDialogsMenuItem.Click += new System.EventHandler(this.toggleDefaultScriptDialogsMenuItem_Click); + this.toggleDefaultScriptDialogsMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; // // blockedDomainsMenuItem // @@ -449,6 +453,7 @@ private void InitializeComponent() this.toggleVisibilityMenuItem.Checked = true; this.toggleVisibilityMenuItem.CheckOnClick = true; this.toggleVisibilityMenuItem.Click += new System.EventHandler(this.toggleVisibilityMenuItem_Click); + this.toggleVisibilityMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; // // zoomToolStripMenuItem // @@ -696,6 +701,7 @@ private void InitializeComponent() this.toggleMuteStateMenuItem.Click += new System.EventHandler(this.toggleMuteStateMenuItem_Click); this.toggleMuteStateMenuItem.Checked = true; this.toggleMuteStateMenuItem.CheckOnClick = true; + this.toggleMuteStateMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; // // helpToolStripMenuItem // diff --git a/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj b/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj index 3899c974..4e4c870b 100644 --- a/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj +++ b/SampleApps/WebView2WindowsFormsBrowser/WebView2WindowsFormsBrowser.csproj @@ -25,7 +25,7 @@ AnyCPU - + diff --git a/SampleApps/WebView2WpfBrowser/Extensions.xaml.cs b/SampleApps/WebView2WpfBrowser/Extensions.xaml.cs index 0aedd53a..dd0a8df0 100644 --- a/SampleApps/WebView2WpfBrowser/Extensions.xaml.cs +++ b/SampleApps/WebView2WpfBrowser/Extensions.xaml.cs @@ -24,9 +24,7 @@ public Extensions(CoreWebView2 coreWebView2) { m_coreWebView2 = coreWebView2; InitializeComponent(); -#if USE_WEBVIEW2_EXPERIMENTAL _ = FillViewAsync(); -#endif } public class ListEntry @@ -43,7 +41,6 @@ public override string ToString() List m_listData = new List(); -#if USE_WEBVIEW2_EXPERIMENTAL private async System.Threading.Tasks.Task FillViewAsync() { IReadOnlyList extensionsList = await m_coreWebView2.Profile.GetBrowserExtensionsAsync(); @@ -60,7 +57,6 @@ private async System.Threading.Tasks.Task FillViewAsync() ExtensionsList.ItemsSource = m_listData; ExtensionsList.Items.Refresh(); } -#endif private void ExtensionsToggleEnabled(object sender, RoutedEventArgs e) { @@ -69,7 +65,6 @@ private void ExtensionsToggleEnabled(object sender, RoutedEventArgs e) private async System.Threading.Tasks.Task ExtensionsToggleEnabledAsync(object sender, RoutedEventArgs e) { -#if USE_WEBVIEW2_EXPERIMENTAL ListEntry entry = (ListEntry)ExtensionsList.SelectedItem; IReadOnlyList extensionsList = await m_coreWebView2.Profile.GetBrowserExtensionsAsync(); bool found = false; @@ -94,9 +89,6 @@ private async System.Threading.Tasks.Task ExtensionsToggleEnabledAsync(object se MessageBox.Show("Failed to find extension"); } await FillViewAsync(); -#else - await Task.CompletedTask; -#endif } private void ExtensionsAdd(object sender, RoutedEventArgs e) @@ -106,7 +98,6 @@ private void ExtensionsAdd(object sender, RoutedEventArgs e) private async System.Threading.Tasks.Task ExtensionsAddAsync(object sender, RoutedEventArgs e) { -#if USE_WEBVIEW2_EXPERIMENTAL var dialog = new TextInputDialog( title: "Add extension", description: "Enter the absolute Windows file path to the unpackaged browser extension", @@ -124,9 +115,6 @@ private async System.Threading.Tasks.Task ExtensionsAddAsync(object sender, Rout MessageBox.Show("Failed to add extension: " + exception); } } -#else - await Task.CompletedTask; -#endif } private void ExtensionsRemove(object sender, RoutedEventArgs e) @@ -136,7 +124,6 @@ private void ExtensionsRemove(object sender, RoutedEventArgs e) private async System.Threading.Tasks.Task ExtensionsRemoveAsync(object sender, RoutedEventArgs e) { -#if USE_WEBVIEW2_EXPERIMENTAL ListEntry entry = (ListEntry)ExtensionsList.SelectedItem; if (MessageBox.Show("Remove extension " + entry + "?", "Confirm removal", MessageBoxButton.OKCancel) == MessageBoxResult.OK) { @@ -164,9 +151,6 @@ private async System.Threading.Tasks.Task ExtensionsRemoveAsync(object sender, R } } await FillViewAsync(); -#else - await Task.CompletedTask; -#endif } } } diff --git a/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs b/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs index 589c8f0f..e162de8d 100644 --- a/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs +++ b/SampleApps/WebView2WpfBrowser/MainWindow.xaml.cs @@ -34,6 +34,7 @@ namespace WebView2WpfBrowser /// public partial class MainWindow : Window { +#region commands public static RoutedCommand InjectScriptCommand = new RoutedCommand(); public static RoutedCommand InjectScriptIFrameCommand = new RoutedCommand(); public static RoutedCommand InjectScriptWithResultCommand = new RoutedCommand(); @@ -85,28 +86,21 @@ public partial class MainWindow : Window public static RoutedCommand PrintToPdfStreamCommand = new RoutedCommand(); // Commands(V2) public static RoutedCommand AboutCommand = new RoutedCommand(); - - - public static RoutedCommand CrashBrowserProcessCommand = new RoutedCommand(); public static RoutedCommand CrashRenderProcessCommand = new RoutedCommand(); - public static RoutedCommand GetDocumentTitleCommand = new RoutedCommand(); public static RoutedCommand GetUserDataFolderCommand = new RoutedCommand(); public static RoutedCommand SharedBufferRequestedCommand = new RoutedCommand(); public static RoutedCommand PostMessageStringCommand = new RoutedCommand(); public static RoutedCommand PostMessageJSONCommand = new RoutedCommand(); - - public static RoutedCommand CloseWebViewCommand = new RoutedCommand(); public static RoutedCommand NewWebViewCommand = new RoutedCommand(); public static RoutedCommand HostObjectsAllowedCommand = new RoutedCommand(); public static RoutedCommand BrowserAcceleratorKeyEnabledCommand = new RoutedCommand(); - public static RoutedCommand AddInitializeScriptCommand = new RoutedCommand(); public static RoutedCommand RemoveInitializeScriptCommand = new RoutedCommand(); @@ -121,9 +115,10 @@ public partial class MainWindow : Window public static RoutedCommand ClearCustomDataPartitionCommand = new RoutedCommand(); public static RoutedCommand ProcessExtendedInfoCommand = new RoutedCommand(); - public static RoutedCommand OpenSaveAsDialogCommand = new RoutedCommand(); - public static RoutedCommand SaveAsSilentCommand = new RoutedCommand(); - + public static RoutedCommand ProgrammaticSaveAsCommand = new RoutedCommand(); + public static RoutedCommand ToggleSilentCommand = new RoutedCommand(); + public static RoutedCommand ThrottlingControlCommand = new RoutedCommand(); +#endregion commands bool _isNavigating = false; @@ -224,7 +219,8 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e) await InitializeWebView(); } - async System.Threading.Tasks.Task InitializeWebView() { + async System.Threading.Tasks.Task InitializeWebView() + { AttachControlEventHandlers(webView); // Set background transparent webView.DefaultBackgroundColor = System.Drawing.Color.Transparent; @@ -2077,11 +2073,10 @@ void WebView_CoreWebView2InitializationCompleted(object sender, CoreWebView2Init } }; main_window.Show(); - } + } }; return; } - // ERROR_DELETE_PENDING(0x8007012f) if (e.InitializationException.HResult == -2147024593) { @@ -2404,7 +2399,8 @@ void PerfInfoCmdExecuted(object target, ExecutedRoutedEventArgs e) // #if USE_WEBVIEW2_EXPERIMENTAL - string AppendFrameInfo(CoreWebView2FrameInfo frameInfo) { + string AppendFrameInfo(CoreWebView2FrameInfo frameInfo) + { string id = frameInfo.FrameId.ToString(); string kind = frameInfo.FrameKind.ToString(); string name = String.IsNullOrEmpty(frameInfo.Name) ? "none" : frameInfo.Name; @@ -2414,14 +2410,16 @@ void PerfInfoCmdExecuted(object target, ExecutedRoutedEventArgs e) CoreWebView2FrameInfo mainFrame = GetAncestorMainFrameInfo(frameInfo); string mainFrameId = mainFrame.FrameId.ToString(); - if (frameInfo == mainFrame) { - type = "main frame"; + if (frameInfo == mainFrame) + { + type = "main frame"; } CoreWebView2FrameInfo childFrame = GetAncestorMainFrameDirectChildFrameInfo(frameInfo); string childFrameId = childFrame == null ? "none" : childFrame.FrameId.ToString(); - if (frameInfo == childFrame) { - type = "first level frame"; + if (frameInfo == childFrame) + { + type = "first level frame"; } return $"{{frame Id:{id} " + @@ -2434,11 +2432,13 @@ void PerfInfoCmdExecuted(object target, ExecutedRoutedEventArgs e) $"| frame Source: \"{source}\"}}\n"; } - CoreWebView2FrameInfo GetAncestorMainFrameInfo(CoreWebView2FrameInfo frameInfo) { - while (frameInfo.ParentFrameInfo != null) { - frameInfo = frameInfo.ParentFrameInfo; - } - return frameInfo; + CoreWebView2FrameInfo GetAncestorMainFrameInfo(CoreWebView2FrameInfo frameInfo) + { + while (frameInfo.ParentFrameInfo != null) + { + frameInfo = frameInfo.ParentFrameInfo; + } + return frameInfo; } // Get the frame's corresponding main frame's direct child frameInfo. @@ -2453,19 +2453,22 @@ void PerfInfoCmdExecuted(object target, ExecutedRoutedEventArgs e) // C GetAncestorMainFrameDirectChildFrameInfo returns C. // D GetAncestorMainFrameDirectChildFrameInfo returns B. // F GetAncestorMainFrameDirectChildFrameInfo returns C. - CoreWebView2FrameInfo GetAncestorMainFrameDirectChildFrameInfo(CoreWebView2FrameInfo frameInfo) { - if (frameInfo.ParentFrameInfo == null) { - return null; - } - - CoreWebView2FrameInfo childFrameInfo = null; - CoreWebView2FrameInfo mainFrameInfo = null; - while (frameInfo != null) { - childFrameInfo = mainFrameInfo; - mainFrameInfo = frameInfo; - frameInfo = frameInfo.ParentFrameInfo; - } - return childFrameInfo; + CoreWebView2FrameInfo GetAncestorMainFrameDirectChildFrameInfo(CoreWebView2FrameInfo frameInfo) + { + if (frameInfo.ParentFrameInfo == null) + { + return null; + } + + CoreWebView2FrameInfo childFrameInfo = null; + CoreWebView2FrameInfo mainFrameInfo = null; + while (frameInfo != null) + { + childFrameInfo = mainFrameInfo; + mainFrameInfo = frameInfo; + frameInfo = frameInfo.ParentFrameInfo; + } + return childFrameInfo; } #endif @@ -2981,7 +2984,6 @@ void BrowserAcceleratorKeyEnabledCommandExecuted(object target, ExecutedRoutedEv async void InjectScriptWithResultCmdExecuted(object target, ExecutedRoutedEventArgs e) { -#if USE_WEBVIEW2_EXPERIMENTAL // var dialog = new TextInputDialog( title: "Inject Script With Result", @@ -3017,9 +3019,6 @@ async void InjectScriptWithResultCmdExecuted(object target, ExecutedRoutedEventA } } // -#else - await Task.CompletedTask; -#endif } string NameOfPermissionKind(CoreWebView2PermissionKind kind) @@ -3292,15 +3291,30 @@ void WebView_NotificationReceived(object sender, CoreWebView2NotificationReceive // #endif - // - void OpenSaveAsDialogExecuted(object target, ExecutedRoutedEventArgs e) + // + async void ProgrammaticSaveAsExecuted(object target, ExecutedRoutedEventArgs e) + { + await Task.Delay(0); + } + // + + // + void ToggleSilentExecuted(object target, ExecutedRoutedEventArgs e) + { + } + // + // Simple function to retrieve fields from a JSON message. + // For production code, you should use a real JSON parser library. + string GetJSONStringField(string jsonMessage, string fieldName) { + string prefix = $"\"{fieldName}\":\""; + var start = jsonMessage.IndexOf(prefix) + prefix.Length; + var end = jsonMessage.Substring(start).IndexOf("\""); + return jsonMessage.Substring(start, end); } - // - // - void SaveAsSilentExecuted(object target, ExecutedRoutedEventArgs e) + + void ThrottlingControlExecuted(object target, ExecutedRoutedEventArgs e) { } - // } -} \ No newline at end of file +} diff --git a/SampleApps/WebView2WpfBrowser/SaveAsDialog.xaml b/SampleApps/WebView2WpfBrowser/SaveAsDialog.xaml index 0c832d64..cc61d5f1 100644 --- a/SampleApps/WebView2WpfBrowser/SaveAsDialog.xaml +++ b/SampleApps/WebView2WpfBrowser/SaveAsDialog.xaml @@ -31,13 +31,13 @@ found in the LICENSE file. -