Conversation
Co-authored-by: David Risney <dave@deletethis.net>
Co-authored-by: David Risney <dave@deletethis.net>
Co-authored-by: David Risney <dave@deletethis.net>
Co-authored-by: David Risney <dave@deletethis.net>
Co-authored-by: David Risney <dave@deletethis.net>
Add spec for MultiProfile.md
| public CreateWebView2ControllerWithOptions(IntPtr parentHWND, string profileName, bool isInPrivate) | ||
| { | ||
| CoreWebView2ControllerOptions options = _webViewEnvironment.CreateCoreWebView2ControllerOptions(profileName, isInPrivate); | ||
| CoreWebView2Controller webView2Controller = await _webViewEnvironment.CreateCoreWebView2ControllerWithOptionsAsync(parentHWND, options); |
There was a problem hiding this comment.
Hopefully the method is overloaded and don't need the "WithOptions"
There was a problem hiding this comment.
Its not but it should be. Add overloads for WinRT and .NET
| String ProfileName, Boolean InPrivateModeEnabled); | ||
|
|
||
| Windows.Foundation.IAsyncOperation<CoreWebView2Controller> | ||
| CreateCoreWebView2ControllerWithOptionsAsync( |
There was a problem hiding this comment.
Add [overload("CreateCoreWebView2ControllerAsync")]
There was a problem hiding this comment.
Yes please add for .NET and WinRT
| CoreWebView2ControllerOptions options); | ||
|
|
||
| Windows.Foundation.IAsyncOperation<CoreWebView2CompositionController> | ||
| CreateCoreWebView2CompositionControllerWithOptionsAsync( |
There was a problem hiding this comment.
Add [overload("CreateCoreWebView2CompositionController")]
There was a problem hiding this comment.
Yes please add for .NET and WinRT
| // ... | ||
|
|
||
| CoreWebView2ControllerOptions CreateCoreWebView2ControllerOptions( | ||
| String ProfileName, Boolean InPrivateModeEnabled); |
There was a problem hiding this comment.
camelCase parameter names ("profileName" and "inPrivateModeEnabled"). (Same in methods below.)
There was a problem hiding this comment.
These parameters were removed in a previous comment above.
| Although you can make your WebView2s use different user data directories to achieve data separation, | ||
| in such way you'll have to be running multiple browser instances (each including a browser process | ||
| and a bunch of child processes), which means much more consumption for system resources including | ||
| memory, CPU footprint, disk space etc. so it is not desirable. |
There was a problem hiding this comment.
From the parenthetical, I think the difference between separate user data folders and separate profiles within the same user data folder is that with separate user data folders, there is no browser process sharing at all. With shared user data but separate profiles, there is process sharing, but no state sharing. This allows reduced CPU and memory usage, since multiple profiles share the same process.
Not clear what the disk space reduction benefit is, though. Do profile share caches?
There was a problem hiding this comment.
Profiles share some state but I'm not sure what state is shared. Please investigate and document.
There was a problem hiding this comment.
I think having multiple user data folders consume more disk space than having just one user data folder containing multiple profile folders. The disk space reduction benefit comes from here. Profiles do not share caches, but we can observe that there're a bunch of sub folders other than profile sub folders under the user data directory.
There was a problem hiding this comment.
Please add to the documentation some examples of content that is shared.
There was a problem hiding this comment.
Thanks! I've added to the documentation. There're some other files and folders in user data directory besides profile folders, such as GrShaderCache(handles caching compiled shaders for a Skia GrContext, about 10MB in my sample), SmartScreen(help keep user's safe while browse the web, about 500KB in my sample) and etc. If we can use multiple profiles sharing one user data directory, we can share these files and folders, thus will save some disk space.
| /// and '#', '@', '$', '(', ')', '+', '-', '_', '~', '.', ' ' (space). | ||
| /// Note: the text must not end with a period '.' or ' ' (space). And, although upper case letters are | ||
| /// allowed, they're treated just as lower case couterparts because the profile name will be mapped to | ||
| /// the real profile directory path on disk and Windows file system handles path names in a case-insensitive way. |
There was a problem hiding this comment.
Presumably that means that leading space is also disallowed. Also, I can't name a profile nul or com1.
There was a problem hiding this comment.
Document the additional restriction that they must be valid filenames (which also means no leading spaces, and nul and com* are disallowed) and then have 'For example: ' and link to what is valid file name document.
Something like:
/// The `ProfileName` property specifies the profile's name. It has a maximum length of 64
/// characters excluding the null terminator and must be a valid file name. See [Naming Files, Paths, and Namespaces](https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file)
/// for more information on file names. It must contain only the following ASCII characters:
/// * alphabet characters: a-z and A-Z
/// * digit characters: 0-9
/// * and '#', '@', '$', '(', ')', '+', '-', '_', '~', '.', ' ' (space).
///
/// Note: the text must not end with a period '.' or ' ' (space) nor start with a ' ' (space). And, although upper case letters are
/// allowed, they're treated the same as their lower case counterparts because the profile name will be mapped to
/// the real profile directory path on disk and Windows file system handles path names in a case-insensitive way.
| m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf()); | ||
| if (hr == E_INVALIDARG) | ||
| { | ||
| ShowFailure(hr, L"Unable to create WebView2 due to an invalid profile name."); |
There was a problem hiding this comment.
Should the sample do its own profile name validation, rather than allowing invalid parameters to be passed through?
There was a problem hiding this comment.
Yes. Apps should either be using hardcoded names (or otherwise computed/generated values) or taking user input. In the first case presumably we don't need to validate the values. In the second case, for user input the app should validate that the input is allowed before calling CreateCoreWebView2ControllerOptions
There was a problem hiding this comment.
The sample uses the user input as ProfileName and will validate ProfileName when calling put_ProfileName function. When calling CreateCoreWebView2ControllerOptions, put_ProfileName will also be raised in it but the default empty string will be passed as the parameter.
For this sample, I think it tells the developer that CreateCoreWebView2ControllerOptions and put_ProfileName will validate the input parameter and will return E_INVALIDARG if passing invalid parameter. And the limitation of ProfileName is on documentation that developers can get easily. If add validation code in sample, it would be almost the same as code in function put_ProfileName. So I'm not sure should I still need to add its own validation code in sample?
| } | ||
|
|
||
| Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options; | ||
| HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions( |
There was a problem hiding this comment.
Since the ProfileName and IsInPrivateModeEnabled are both settable properties, do we even need these extra constructor parameters? Can they just default to L"Default" and false? That would align with any future Options properties we add later. For example, maybe you want to use the default profile in InPrivate mode. Or we add some future property like "UseRemoteHolographic" and you want to create a default profile, but with remote holographic support.
There was a problem hiding this comment.
Yes let's have no in parameters to HRESULT CreateCoreWebView2ControllerOptions(ICoreWebView2ControllerOptions**) and just use the settable properties to make this consistent with future properties we add to the profile. The value for profile name should be the empty string, and the default value for IsInPrivate should be false.
There was a problem hiding this comment.
Good idea! I changed the interface to accept only ICoreWebView2ControllerOptions parameter. Please help check. Thanks.
| String ProfilePath { get; }; | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
There appears to be no way to enumerate profiles or delete unwanted ones. Is the only way to delete a profile to delete the entire user data folder (which deletes all the profiles)?
There was a problem hiding this comment.
We will consider adding based on customer feedback.
|
|
||
| Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options; | ||
| HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions( | ||
| m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf()); |
There was a problem hiding this comment.
From what I can tell, this is the only way to create an InPrivate WebView: To create a profile in InPrivate mode. Is that right?
There was a problem hiding this comment.
Correct. Document better how inPrivate interacts with profiles.
There was a problem hiding this comment.
As WebView2 is built on top of Edge browser, it follows Edge's behavior pattern. To create an InPrivate WebView, we gets an off-the-record profile (an InPrivate profile) from a regular profile, then create the WebView with the off-the-record profile.
|
|
||
| Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options; | ||
| HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions( | ||
| m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf()); |
There was a problem hiding this comment.
What happens if I create a Webview2Controller with { Profile="Shopping", InPrivate = false } and then later create another one with { Profile = "Shopping", InPrivate = true }? Is that legal?
What happens if I create a Webview2Controller with { Profile="Shopping", InPrivate = true } and never create a non-InPrivate profile. When the WebView closes, is the profile destroyed? Or does the profile still hang around, with nothing in it?
There was a problem hiding this comment.
Must investigate and document this.
In the first case, we create two controllers using the same profile but one with InPrivate set and one not. Are both allowed to run at the same time? I assume so because it works like that for the browser. Please investigate and document it in the reference documentation for CreateCoreWebView2ControllerWithOptions.
In the second case, what happens if you create a profile with InPrivate set and that is the first time the profile gets created? Will the profile exist on disk once the corresponding controller is closed? Please investigate and document in the reference documentation for CreateCoreWebView2ControllerWithOptions.
There was a problem hiding this comment.
Yes in the first case, they are both allowed to run at the same time.
In the second case, the profile will be created on disk at the first time, and it will be released in memory when the corresponding controller is closed, but will still remain on disk.
|
|
||
| Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options; | ||
| HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions( | ||
| m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf()); |
There was a problem hiding this comment.
Is there a way to tell the WV2 to put the profile in a directory under the app's control? That way - especially for Desktop Bridge apps - the app/system can remove the profile directory during it uninstall phase.
There was a problem hiding this comment.
Great question: yes indirectly. Profiles are sub folders under the CoreWebView2's user data folder. The host app optionally provides the path for the user data folder when creating the CoreWebView2 environment. WinUI doesn't provide a way for the end dev to set the user data folder right now but there's a scenario for it on them.
There was a problem hiding this comment.
The profile is a subdirectory of the user data directory, and the app got to choose its user data directory location.
| m_profileDirName = str.substr(str.find_last_of(L'\\') + 1); | ||
|
|
||
| // update window title with m_profileDirName | ||
| UpdateAppTitle(); |
There was a problem hiding this comment.
I guess the sample could show the uninstall making one last profile so it can find and delete the content?
| interface ICoreWebView2Profile; | ||
|
|
||
| [uuid(C2669A3A-03A9-45E9-97EA-03CD55E5DC03), object, pointer_default(unique)] | ||
| interface ICoreWebView2ControllerOptions : IUnknown { |
There was a problem hiding this comment.
Is there a point at which we'll convert the interface to MIDL3, so you don't have to dual-author the C++ and C# interop types? We'll have Microsoft.Windows.UI.Core.WindowId at some point so you don't have to work around HWND missing in WinRT metadata.
There was a problem hiding this comment.
TLDR: In the long term yes I'd like to make our IDL3 the source of truth and from it generate our .NET & COM APIs or replace those with just WinRT
This is our COM IDL(1) style IDL file to define our COM APIs usable down to Win7. Because it goes to Win7 where we don't have various aspects of WinRT available the conventions of the COM API are similar to but are not the same as the WinRT ABI. We later added WinRT support in part by generating a MIDL3 from our COM IDL in a tool that understands how to translate between our conventions and WinRT. In the future I'd like to make MIDL3 the source of truth from which we generate our COM IDL and then after that if usage drops far enough deprecating in favor of COM ABI WinRT.
| Although you can make your WebView2s use different user data directories to achieve data separation, | ||
| in such way you'll have to be running multiple browser instances (each including a browser process | ||
| and a bunch of child processes), which means much more consumption for system resources including | ||
| memory, CPU footprint, disk space etc. so it is not desirable. |
There was a problem hiding this comment.
Profiles share some state but I'm not sure what state is shared. Please investigate and document.
| } | ||
|
|
||
| Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options; | ||
| HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions( |
There was a problem hiding this comment.
Yes let's have no in parameters to HRESULT CreateCoreWebView2ControllerOptions(ICoreWebView2ControllerOptions**) and just use the settable properties to make this consistent with future properties we add to the profile. The value for profile name should be the empty string, and the default value for IsInPrivate should be false.
|
|
||
| Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options; | ||
| HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions( | ||
| m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf()); |
There was a problem hiding this comment.
Correct. Document better how inPrivate interacts with profiles.
| // ... | ||
|
|
||
| CoreWebView2ControllerOptions CreateCoreWebView2ControllerOptions( | ||
| String ProfileName, Boolean InPrivateModeEnabled); |
There was a problem hiding this comment.
These parameters were removed in a previous comment above.
| String ProfileName, Boolean InPrivateModeEnabled); | ||
|
|
||
| Windows.Foundation.IAsyncOperation<CoreWebView2Controller> | ||
| CreateCoreWebView2ControllerWithOptionsAsync( |
There was a problem hiding this comment.
Yes please add for .NET and WinRT
| CoreWebView2ControllerOptions options); | ||
|
|
||
| Windows.Foundation.IAsyncOperation<CoreWebView2CompositionController> | ||
| CreateCoreWebView2CompositionControllerWithOptionsAsync( |
There was a problem hiding this comment.
Yes please add for .NET and WinRT
| String ProfilePath { get; }; | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
We will consider adding based on customer feedback.
|
|
||
| Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options; | ||
| HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions( | ||
| m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf()); |
There was a problem hiding this comment.
Great question: yes indirectly. Profiles are sub folders under the CoreWebView2's user data folder. The host app optionally provides the path for the user data folder when creating the CoreWebView2 environment. WinUI doesn't provide a way for the end dev to set the user data folder right now but there's a scenario for it on them.
| interface ICoreWebView2Profile; | ||
|
|
||
| [uuid(C2669A3A-03A9-45E9-97EA-03CD55E5DC03), object, pointer_default(unique)] | ||
| interface ICoreWebView2ControllerOptions : IUnknown { |
There was a problem hiding this comment.
TLDR: In the long term yes I'd like to make our IDL3 the source of truth and from it generate our .NET & COM APIs or replace those with just WinRT
This is our COM IDL(1) style IDL file to define our COM APIs usable down to Win7. Because it goes to Win7 where we don't have various aspects of WinRT available the conventions of the COM API are similar to but are not the same as the WinRT ABI. We later added WinRT support in part by generating a MIDL3 from our COM IDL in a tool that understands how to translate between our conventions and WinRT. In the future I'd like to make MIDL3 the source of truth from which we generate our COM IDL and then after that if usage drops far enough deprecating in favor of COM ABI WinRT.
Co-authored-by: David Risney <dave@deletethis.net> Co-authored-by: Raymond Chen <oldnewthing@users.noreply.github.com>
This is an API review for WebView2 multiple profile support. Thanks.