Skip to content

Commit bd0048f

Browse files
Merge pull request #299 from MicrosoftEdge/smoketest/1.0.4255-testing
Smoketest/1.0.4255 testing
2 parents 22f2fd1 + 62f084f commit bd0048f

14 files changed

Lines changed: 874 additions & 13 deletions

‎SampleApps/WebView2APISample/App.cpp‎

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
#include <vector>
1515

1616
#include "AppWindow.h"
17+
#include "CheckFailure.h"
1718
#include "DpiUtil.h"
19+
#include "ScenarioClusterEnvironment.h"
1820

1921
HINSTANCE g_hInstance;
2022
int g_nCmdShow;
@@ -44,6 +46,12 @@ wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmd
4446
DWORD creationModeId = IDM_CREATION_MODE_WINDOWED;
4547
WebViewCreateOption opt;
4648

49+
// When launched with --clustername=..., this instance is a secondary host
50+
// process that should join an existing shared cluster environment instead
51+
// of creating a normal (private) environment.
52+
bool joinCluster = false;
53+
ClusterEnvironmentSpec clusterSpec;
54+
4755
if (lpCmdLine && lpCmdLine[0])
4856
{
4957
int paramCount = 0;
@@ -94,6 +102,64 @@ wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmd
94102
{
95103
userDataFolder = nextParam.substr(nextParam.find(L'=') + 1);
96104
}
105+
else if (NEXT_PARAM_CONTAINS(L"clustername="))
106+
{
107+
joinCluster = true;
108+
clusterSpec.clusterName = nextParam.substr(nextParam.find(L'=') + 1);
109+
}
110+
else if (NEXT_PARAM_CONTAINS(L"clusterlang="))
111+
{
112+
clusterSpec.language = nextParam.substr(nextParam.find(L'=') + 1);
113+
}
114+
else if (NEXT_PARAM_CONTAINS(L"clusterargs="))
115+
{
116+
clusterSpec.additionalBrowserArguments =
117+
nextParam.substr(nextParam.find(L'=') + 1);
118+
}
119+
else if (NEXT_PARAM_CONTAINS(L"clustersso="))
120+
{
121+
clusterSpec.allowSingleSignOn =
122+
nextParam.substr(nextParam.find(L'=') + 1) == L"1";
123+
}
124+
else if (NEXT_PARAM_CONTAINS(L"clustertracking="))
125+
{
126+
clusterSpec.enableTrackingPrevention =
127+
nextParam.substr(nextParam.find(L'=') + 1) == L"1";
128+
}
129+
else if (NEXT_PARAM_CONTAINS(L"clusterextensions="))
130+
{
131+
clusterSpec.areBrowserExtensionsEnabled =
132+
nextParam.substr(nextParam.find(L'=') + 1) == L"1";
133+
}
134+
else if (NEXT_PARAM_CONTAINS(L"clusterisolation="))
135+
{
136+
clusterSpec.perHostProfileIsolation =
137+
nextParam.substr(nextParam.find(L'=') + 1) == L"1";
138+
}
139+
else if (NEXT_PARAM_CONTAINS(L"clusterchannels="))
140+
{
141+
// wcstol rather than stoi: a malformed value should fall back to
142+
// 0 (NONE) rather than throw out of command-line parsing. Masked
143+
// because the value is cast to a flags enum, and a negative or
144+
// oversized number would otherwise set every reserved bit.
145+
const long channels =
146+
wcstol(nextParam.substr(nextParam.find(L'=') + 1).c_str(), nullptr, 10);
147+
clusterSpec.releaseChannels =
148+
static_cast<COREWEBVIEW2_RELEASE_CHANNELS>(channels & kAllReleaseChannels);
149+
}
150+
else if (NEXT_PARAM_CONTAINS(L"clustersearchkind="))
151+
{
152+
// Parsed the same way the parent writes it, rather than by
153+
// testing for L"1", so a value added to the enum later does not
154+
// silently arrive as MOST_STABLE. Clamped for the same reason
155+
// the channels are masked: this is cast to an enum.
156+
const long searchKind =
157+
wcstol(nextParam.substr(nextParam.find(L'=') + 1).c_str(), nullptr, 10);
158+
clusterSpec.channelSearchKind =
159+
searchKind == COREWEBVIEW2_CHANNEL_SEARCH_KIND_LEAST_STABLE
160+
? COREWEBVIEW2_CHANNEL_SEARCH_KIND_LEAST_STABLE
161+
: COREWEBVIEW2_CHANNEL_SEARCH_KIND_MOST_STABLE;
162+
}
97163
else if (NEXT_PARAM_CONTAINS(L"creationmode="))
98164
{
99165
nextParam = nextParam.substr(nextParam.find(L'=') + 1);
@@ -125,7 +191,18 @@ wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmd
125191

126192
DpiUtil::SetProcessDpiAwarenessContext(dpiAwarenessContext);
127193

128-
new AppWindow(creationModeId, opt, initialUri, userDataFolder, true);
194+
if (joinCluster)
195+
{
196+
// COM is required before calling into the loader; AppWindow (created in
197+
// the completion handler) will also initialize COM on this thread.
198+
CHECK_FAILURE(OleInitialize(nullptr));
199+
CreateOrJoinClusterAndOpenWindow(
200+
clusterSpec, /*isMainWindow=*/true, /*parent=*/nullptr);
201+
}
202+
else
203+
{
204+
new AppWindow(creationModeId, opt, initialUri, userDataFolder, true);
205+
}
129206

130207
int retVal = RunMessagePump();
131208

‎SampleApps/WebView2APISample/AppStartPage.cpp‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,62 @@ std::wstring GetRuntimePath(AppWindow* appWindow)
8686
return ResolvePathAndTrimFile(runtimePath);
8787
}
8888

89+
// Returns the actual user data folder in use by the current WebView2
90+
// environment. Empty if the environment doesn't support querying it
91+
// (ICoreWebView2Environment7 unavailable).
92+
std::wstring GetUserDataFolder(AppWindow* appWindow)
93+
{
94+
wil::com_ptr<ICoreWebView2Environment> environment = appWindow->GetWebViewEnvironment();
95+
auto environment7 = environment.try_query<ICoreWebView2Environment7>();
96+
if (!environment7)
97+
return L"";
98+
99+
wil::unique_cotaskmem_string userDataFolder;
100+
if (FAILED(environment7->get_UserDataFolder(&userDataFolder)) || !userDataFolder)
101+
return L"";
102+
return userDataFolder.get();
103+
}
104+
105+
// Classifies a user data folder as shared when it lives under the shared
106+
// cluster root, mirroring the detection the browser itself uses.
107+
std::wstring GetUserDataFolderKind(const std::wstring& userDataFolder)
108+
{
109+
if (userDataFolder.empty())
110+
return L"Unknown";
111+
112+
std::wstring lowerUserDataFolder = userDataFolder;
113+
std::transform(
114+
lowerUserDataFolder.begin(), lowerUserDataFolder.end(), lowerUserDataFolder.begin(),
115+
::towlower);
116+
return lowerUserDataFolder.find(L"\\microsoft\\webview2clusters\\") != std::wstring::npos
117+
? L"Shared (cluster)"
118+
: L"Non-shared";
119+
}
120+
121+
// Percent-encodes the characters that would otherwise be read as query string
122+
// syntax. The folder carries a user-chosen name that may contain '&' or '=',
123+
// either of which would split the value and blank the field.
124+
std::wstring EscapeForQuery(const std::wstring& value)
125+
{
126+
static constexpr wchar_t kHexDigits[] = L"0123456789ABCDEF";
127+
std::wstring escaped;
128+
escaped.reserve(value.size());
129+
for (const wchar_t c : value)
130+
{
131+
if (c == L'&' || c == L'=' || c == L'%' || c == L'#' || c == L'?' || c == L'+')
132+
{
133+
escaped.push_back(L'%');
134+
escaped.push_back(kHexDigits[(c >> 4) & 0xF]);
135+
escaped.push_back(kHexDigits[c & 0xF]);
136+
}
137+
else
138+
{
139+
escaped.push_back(c);
140+
}
141+
}
142+
return escaped;
143+
}
144+
89145
std::wstring GetUri(AppWindow* appWindow)
90146
{
91147
std::wstring uri = appWindow->GetLocalUri(L"AppStartPage.html", true);
@@ -102,6 +158,13 @@ std::wstring GetUri(AppWindow* appWindow)
102158
uri += L"&runtimePath=";
103159
uri += GetRuntimePath(appWindow);
104160

161+
std::wstring userDataFolder = GetUserDataFolder(appWindow);
162+
uri += L"&userDataFolder=";
163+
uri += EscapeForQuery(userDataFolder);
164+
165+
uri += L"&userDataFolderKind=";
166+
uri += GetUserDataFolderKind(userDataFolder);
167+
105168
return uri;
106169
}
107170

‎SampleApps/WebView2APISample/AppWindow.cpp‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "ScenarioAddHostObject.h"
3232
#include "ScenarioAuthentication.h"
3333
#include "ScenarioClientCertificateRequested.h"
34+
#include "ScenarioClusterEnvironment.h"
3435
#include "ScenarioCookieManagement.h"
3536
#include "ScenarioCustomDownloadExperience.h"
3637
#include "ScenarioCustomScheme.h"
@@ -203,9 +204,10 @@ AppWindow::AppWindow(
203204
UINT creationModeId, const WebViewCreateOption& opt, const std::wstring& initialUri,
204205
const std::wstring& userDataFolderParam, bool isMainWindow,
205206
std::function<void()> webviewCreatedCallback, bool customWindowRect, RECT windowRect,
206-
bool shouldHaveToolbar, bool isPopup)
207+
bool shouldHaveToolbar, bool isPopup, ICoreWebView2Environment* providedEnvironment)
207208
: m_creationModeId(creationModeId), m_webviewOption(opt), m_initialUri(initialUri),
208-
m_onWebViewFirstInitialized(webviewCreatedCallback), m_isPopupWindow(isPopup)
209+
m_onWebViewFirstInitialized(webviewCreatedCallback), m_isPopupWindow(isPopup),
210+
m_providedEnvironment(providedEnvironment)
209211
{
210212
// Initialize COM as STA.
211213
CHECK_FAILURE(OleInitialize(NULL));
@@ -648,6 +650,18 @@ bool AppWindow::ExecuteWebViewCommands(WPARAM wParam, LPARAM lParam)
648650
NewComponent<ScenarioCustomScheme>(this);
649651
return true;
650652
}
653+
case IDM_SCENARIO_CLUSTER_ENVIRONMENT:
654+
{
655+
NewComponent<ScenarioClusterEnvironment>(
656+
this, ScenarioClusterEnvironment::Mode::CreateOrJoin);
657+
return true;
658+
}
659+
case IDM_SCENARIO_CLUSTER_ENVIRONMENT_GET_OPTIONS:
660+
{
661+
NewComponent<ScenarioClusterEnvironment>(
662+
this, ScenarioClusterEnvironment::Mode::GetOptions);
663+
return true;
664+
}
651665
case IDM_SCENARIO_CUSTOM_SCHEME_NAVIGATE:
652666
{
653667
NewComponent<ScenarioCustomSchemeNavigate>(this);
@@ -1841,6 +1855,14 @@ void AppWindow::InitializeWebView()
18411855
}
18421856
//! [CreateCoreWebView2EnvironmentWithOptions]
18431857

1858+
// When a pre-created environment was provided, host the WebView in it
1859+
// directly instead of creating a new environment.
1860+
if (m_providedEnvironment)
1861+
{
1862+
OnCreateEnvironmentCompleted(S_OK, m_providedEnvironment.get());
1863+
return;
1864+
}
1865+
18441866
std::wstring args;
18451867
// Page Interaction Restriction Manager requires msPageInteractionManagerWebview2 to be
18461868
// enabled from the args, as by default it's disabled in the browser. If you want to

‎SampleApps/WebView2APISample/AppWindow.h‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class AppWindow
104104
const std::wstring& initialUri = L"", const std::wstring& userDataFolderParam = L"",
105105
bool isMainWindow = false, std::function<void()> webviewCreatedCallback = nullptr,
106106
bool customWindowRect = false, RECT windowRect = {0}, bool shouldHaveToolbar = true,
107-
bool isPopup = false);
107+
bool isPopup = false, ICoreWebView2Environment* providedEnvironment = nullptr);
108108

109109
~AppWindow();
110110

@@ -269,6 +269,10 @@ class AppWindow
269269
int m_refCount = 1;
270270
bool m_isClosed = false;
271271

272+
// When non-null, InitializeWebView uses this pre-created environment
273+
// instead of creating its own via CreateCoreWebView2EnvironmentWithOptions.
274+
wil::com_ptr<ICoreWebView2Environment> m_providedEnvironment;
275+
272276
// The following is state that belongs with the webview, and should
273277
// be reinitialized along with it. Everything here is undefined when
274278
// m_webView is null.

0 commit comments

Comments
 (0)