Skip to content

Commit b6da5fb

Browse files
authored
Merge pull request #1717 from MicrosoftEdge/api-multiprofile
API Review: multiple profile support
2 parents 75c91a7 + c23c19c commit b6da5fb

1 file changed

Lines changed: 307 additions & 0 deletions

File tree

specs/MultiProfile.md

Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
API spec for multiple profile support
2+
===
3+
4+
# Background
5+
6+
Previously, all WebView2s can only use one fixed Edge profile in the browser process, which is
7+
normally the **Default** profile by not specifying a profile path, or the profile specified by the
8+
**--profile-directory** command line switch. It means different WebView2s share a single profile
9+
directory on disk for data storage, which might bring security concerns over cookies, autofill data,
10+
and password management etc.. Also, they might also interfere with each other in terms of user
11+
preference settings.
12+
13+
Although you can make your WebView2s use different user data directories to achieve data separation,
14+
in such way you'll have to be running multiple browser instances (each including a browser process
15+
and a bunch of child processes), which means much more consumption for system resources including
16+
memory, CPU footprint, disk space (profiles under a single user data directory share several types
17+
of data, such as compiled shaders cached for a Skia GrContext and safebrowsing data) etc. so it is
18+
not desirable.
19+
20+
With all above, we're adding these new APIs to support multiple profiles, so that you can have
21+
multiple WebView2s running with separate profiles under a single user data directory (i.e. a single
22+
browser instance at runtime), which means separate cookies, user preference settings, and various
23+
data storage etc., to help you build a more wonderful experience for your application.
24+
25+
# Examples
26+
27+
## Win32 C++
28+
29+
### Provide options to create WebView2 with a specific profile
30+
31+
```cpp
32+
HRESULT AppWindow::CreateControllerWithOptions()
33+
{
34+
auto webViewEnvironment4 =
35+
m_webViewEnvironment.try_query<ICoreWebView2Environment4>();
36+
if (!webViewEnvironment4)
37+
{
38+
FeatureNotAvailable();
39+
return S_OK;
40+
}
41+
42+
wil::com_ptr<ICoreWebView2ControllerOptions> options;
43+
// The validation of parameters occurs when setting the properties.
44+
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(options.GetAddressOf());
45+
if (hr == E_INVALIDARG)
46+
{
47+
ShowFailure(hr, L"Unable to create WebView2 due to an invalid profile name.");
48+
CloseAppWindow();
49+
return S_OK;
50+
}
51+
CHECK_FAILURE(hr);
52+
53+
// If call 'put_ProfileName' with an invalid profile name, the 'E_INVALIDARG' returned immediately.
54+
// ProfileName could be reused.
55+
CHECK_FAILURE(options->put_ProfileName(m_webviewOption.profile.c_str()));
56+
CHECK_FAILURE(options->put_IsInPrivateModeEnabled(m_webviewOption.isInPrivate));
57+
58+
if (m_dcompDevice || m_wincompCompositor)
59+
{
60+
CHECK_FAILURE(webViewEnvironment4->CreateCoreWebView2CompositionControllerWithOptions(
61+
m_mainWindow, options.Get(),
62+
Callback<ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler>(
63+
[this](
64+
HRESULT result,
65+
ICoreWebView2CompositionController* compositionController) -> HRESULT {
66+
auto controller =
67+
wil::com_ptr<ICoreWebView2CompositionController>(compositionController)
68+
.query<ICoreWebView2Controller>();
69+
return OnCreateCoreWebView2ControllerCompleted(result, controller.get());
70+
})
71+
.Get()));
72+
}
73+
else
74+
{
75+
CHECK_FAILURE(webViewEnvironment4->CreateCoreWebView2ControllerWithOptions(
76+
m_mainWindow, options.Get(),
77+
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
78+
this, &AppWindow::OnCreateCoreWebView2ControllerCompleted)
79+
.Get()));
80+
}
81+
82+
return S_OK;
83+
}
84+
```
85+
86+
### Access the profile property of WebView2
87+
88+
```cpp
89+
HRESULT AppWindow::OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller)
90+
{
91+
// ...
92+
93+
m_controller = controller;
94+
95+
// Gets the webview object from controller.
96+
wil::com_ptr<ICoreWebView2> coreWebView2;
97+
CHECK_FAILURE(m_controller->get_CoreWebView2(&coreWebView2));
98+
99+
auto webview7 = coreWebView2.try_query<ICoreWebView2_7>();
100+
if (webview7)
101+
{
102+
// Gets the profile property of webview.
103+
wil::com_ptr<ICoreWebView2Profile> profile;
104+
CHECK_FAILURE(webview7->get_Profile(&profile));
105+
106+
// Accesses the profile object.
107+
BOOL inPrivateModeEnabled = FALSE;
108+
CHECK_FAILURE(profile->get_IsInPrivateModeEnabled(&inPrivateModeEnabled));
109+
CHECK_FAILURE(profile->get_ProfileName(&m_profileName));
110+
111+
// update window title with m_profileName
112+
UpdateAppTitle();
113+
114+
// update window icon
115+
SetAppIcon(inPrivateModeEnabled);
116+
}
117+
118+
// ...
119+
}
120+
```
121+
## .NET and WinRT
122+
123+
### Create WebView2 with a specific profile, then access the profile property of WebView2
124+
125+
```csharp
126+
CoreWebView2Environment _webViewEnvironment;
127+
WebViewCreationOptions _creationOptions;
128+
public CreateWebView2Controller(IntPtr parentWindow)
129+
{
130+
CoreWebView2ControllerOptions controllerOptions = new CoreWebView2ControllerOptions();
131+
controllerOptions.ProfileName = _creationOptions.profileName;
132+
controllerOptions.IsInPrivateModeEnabled = _creationOptions.IsInPrivateModeEnabled;
133+
134+
CoreWebView2Controller controller = null;
135+
136+
if (_creationOptions.entry == WebViewCreateEntry.CREATE_WITH_OPTION)
137+
{
138+
controller = await _webViewEnvironment.CreateCoreWebView2ControllerAsync(parentWindow, options);
139+
}
140+
else
141+
{
142+
controller = await _webViewEnvironment.CreateCoreWebView2ControllerAsync(parentWindow);
143+
}
144+
145+
string profileName = controller.CoreWebView2.Profile.ProfileName;
146+
bool inPrivate = controller.CoreWebView2.Profile.IsInPrivateModeEnabled;
147+
148+
// update window title with profileName
149+
UpdateAppTitle(profileName);
150+
151+
// update window icon
152+
SetAppIcon(inPrivate);
153+
}
154+
```
155+
156+
# API Details
157+
158+
## Win32 C++
159+
160+
```IDL
161+
interface ICoreWebView2ControllerOptions;
162+
interface ICoreWebView2Environment5;
163+
interface ICoreWebView2_7;
164+
interface ICoreWebView2Profile;
165+
166+
/// This interface is used to manage profile options that created by 'CreateCoreWebView2ControllerOptions'.
167+
[uuid(C2669A3A-03A9-45E9-97EA-03CD55E5DC03), object, pointer_default(unique)]
168+
interface ICoreWebView2ControllerOptions : IUnknown {
169+
/// The `ProfileName` property specifies the profile's name. It has a maximum length of 64
170+
/// characters excluding the null terminator and must be a valid file name.
171+
/// See [Naming Files, Paths, and Namespaces](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file)
172+
/// for more information on file names. It must contain only the following ASCII characters:
173+
///
174+
/// * alphabet characters: a-z and A-Z
175+
/// * digit characters: 0-9
176+
/// * and '#', '@', '$', '(', ')', '+', '-', '_', '~', '.', ' ' (space).
177+
///
178+
/// Note: the text must not end with a period '.' or ' ' (space) nor start with a ' ' (space). And, although upper
179+
/// case letters are allowed, they're treated the same as their lower case counterparts because the profile name
180+
/// will be mapped to the real profile directory path on disk and Windows file system handles path names in a
181+
/// case-insensitive way.
182+
[propget] HRESULT ProfileName([out, retval] LPWSTR* value);
183+
/// Sets the `ProfileName` property.
184+
[propput] HRESULT ProfileName([in] LPCWSTR value);
185+
186+
/// `IsInPrivateModeEnabled` property is to enable/disable InPrivate mode.
187+
[propget] HRESULT IsInPrivateModeEnabled([out, retval] BOOL* value);
188+
/// Sets the `IsInPrivateModeEnabled` property.
189+
[propput] HRESULT IsInPrivateModeEnabled([in] BOOL value);
190+
}
191+
192+
/// This interface is used to create 'CreateCoreWebView2ControllerOptions' object, which
193+
/// can be passed as a parameter in 'CreateCoreWebView2ControllerWithOptions' and
194+
/// 'CreateCoreWebView2CompositionControllerWithOptions' function for multiple profile support.
195+
/// The profile will be created on disk or opened when calling 'CreateCoreWebView2ControllerWithOptions' or
196+
/// 'CreateCoreWebView2CompositionControllerWithOptions' no matter InPrivate mode is enabled or not, and it will be
197+
/// released in memory when the corresponding controller is closed but still remain on disk.
198+
/// If create a WebView2Controller with {ProfileName="name", InPrivate=false} and then later create another one with
199+
/// one with {ProfileName="name", InPrivate=true}, these two controllers using the same profile would be allowed to
200+
/// run at the same time.
201+
/// As WebView2 is built on top of Edge browser, it follows Edge's behavior pattern. To create an InPrivate WebView,
202+
/// we gets an off-the-record profile (an InPrivate profile) from a regular profile, then create the WebView with the
203+
/// off-the-record profile.
204+
[uuid(57FD205C-39D5-4BA1-8E7B-3E53C323EA87), object, pointer_default(unique)]
205+
interface ICoreWebView2Environment5 : IUnknown {
206+
/// Create a new ICoreWebView2ControllerOptions to be passed as a parameter of
207+
/// CreateCoreWebView2ControllerWithOptions and CreateCoreWebView2CompositionControllerWithOptions.
208+
/// The 'options' is settable and in it the default value for profile name is the empty string,
209+
/// and the default value for IsInPrivateModeEnabled is false.
210+
/// Also the profile name can be reused.
211+
HRESULT CreateCoreWebView2ControllerOptions(
212+
[out, retval] ICoreWebView2ControllerOptions** options);
213+
214+
/// Create a new WebView with options.
215+
HRESULT CreateCoreWebView2ControllerWithOptions(
216+
[in] HWND parentWindow,
217+
[in] ICoreWebView2ControllerOptions* options,
218+
[in] ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* handler);
219+
220+
/// Create a new WebView in visual hosting mode with options.
221+
HRESULT CreateCoreWebView2CompositionControllerWithOptions(
222+
[in] HWND parentWindow,
223+
[in] ICoreWebView2ControllerOptions* options,
224+
[in] ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler* handler);
225+
}
226+
227+
/// Used to get ICoreWebView2Profile object.
228+
[uuid(6E5CE5F0-16E6-4A05-97D8-4E256B3EB609), object, pointer_default(unique)]
229+
interface ICoreWebView2_7 : IUnknown {
230+
/// The associated `ICoreWebView2Profile` object. If this CoreWebView2 was created with a
231+
/// CoreWebView2ControllerOptions, the CoreWebView2Profile will match those specified options.
232+
/// Otherwise if this CoreWebView2 was created without a CoreWebView2ControllerOptions, then
233+
/// this will be the default CoreWebView2Profile for the corresponding CoreWebView2Environment.
234+
[propget] HRESULT Profile([out, retval] ICoreWebView2Profile** value);
235+
}
236+
237+
[uuid(3B9A2AF2-E703-4C81-9D25-FCE44312E960), object, pointer_default(unique)]
238+
interface ICoreWebView2Profile : IUnknown {
239+
/// Name of the profile.
240+
[propget] HRESULT ProfileName([out, retval] LPWSTR* value);
241+
242+
/// InPrivate mode is enabled or not.
243+
[propget] HRESULT IsInPrivateModeEnabled([out, retval] BOOL* value);
244+
245+
/// Full path of the profile directory.
246+
[propget] HRESULT ProfilePath([out, retval] LPWSTR* value);
247+
248+
// TODO: All profile-wide operations/settings will be put below in the future.
249+
}
250+
```
251+
252+
## .NET and WinRT
253+
254+
```c#
255+
namespace Microsoft.Web.WebView2.Core
256+
{
257+
runtimeclass CoreWebView2ControllerOptions;
258+
runtimeclass CoreWebView2Environment;
259+
runtimeclass CoreWebView2;
260+
runtimeclass CoreWebView2Profile;
261+
262+
runtimeclass CoreWebView2ControllerOptions
263+
{
264+
String ProfileName { get; set; };
265+
266+
Boolean IsInPrivateModeEnabled { get; set; };
267+
}
268+
269+
runtimeclass CoreWebView2Environment
270+
{
271+
// ...
272+
273+
CoreWebView2ControllerOptions CreateCoreWebView2ControllerOptions();
274+
275+
Windows.Foundation.IAsyncOperation<CoreWebView2Controller>
276+
CreateCoreWebView2ControllerAsync(
277+
CoreWebView2ControllerWindowReference ParentWindow,
278+
CoreWebView2ControllerOptions options);
279+
280+
Windows.Foundation.IAsyncOperation<CoreWebView2CompositionController>
281+
CreateCoreWebView2CompositionControllerAsync(
282+
CoreWebView2ControllerWindowReference ParentWindow,
283+
CoreWebView2ControllerOptions options);
284+
}
285+
286+
runtimeclass CoreWebView2
287+
{
288+
// ...
289+
CoreWebView2Profile Profile { get; };
290+
}
291+
292+
runtimeclass CoreWebView2Profile
293+
{
294+
String ProfileName { get; };
295+
296+
Boolean IsInPrivateModeEnabled { get; };
297+
298+
String ProfilePath { get; };
299+
}
300+
}
301+
```
302+
303+
# Appendix
304+
305+
Next we'll consolidate all profile-wide operations/settings into the interface
306+
`ICoreWebView2Profile`, and will also add support for erasing a profile completely
307+
if strongly requested.

0 commit comments

Comments
 (0)