Skip to content

API Review: multiple profile support#1717

Merged
lichen63 merged 36 commits intomasterfrom
api-multiprofile
Oct 8, 2021
Merged

API Review: multiple profile support#1717
lichen63 merged 36 commits intomasterfrom
api-multiprofile

Conversation

@leonhsl
Copy link
Copy Markdown
Contributor

@leonhsl leonhsl commented Sep 8, 2021

This is an API review for WebView2 multiple profile support. Thanks.

@leonhsl leonhsl requested a review from david-risney September 8, 2021 03:40
@leonhsl leonhsl added the API Proposal Review WebView2 API Proposal for review. label Sep 8, 2021
@leonhsl leonhsl self-assigned this Sep 8, 2021
Comment thread specs/MultiProfile.md
Comment thread specs/MultiProfile.md Outdated
Comment thread specs/MultiProfile.md Outdated
Comment thread specs/MultiProfile.md
Comment thread specs/MultiProfile.md Outdated
public CreateWebView2ControllerWithOptions(IntPtr parentHWND, string profileName, bool isInPrivate)
{
CoreWebView2ControllerOptions options = _webViewEnvironment.CreateCoreWebView2ControllerOptions(profileName, isInPrivate);
CoreWebView2Controller webView2Controller = await _webViewEnvironment.CreateCoreWebView2ControllerWithOptionsAsync(parentHWND, options);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully the method is overloaded and don't need the "WithOptions"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not but it should be. Add overloads for WinRT and .NET

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread specs/MultiProfile.md
Comment thread specs/MultiProfile.md Outdated
String ProfileName, Boolean InPrivateModeEnabled);

Windows.Foundation.IAsyncOperation<CoreWebView2Controller>
CreateCoreWebView2ControllerWithOptionsAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add [overload("CreateCoreWebView2ControllerAsync")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please add for .NET and WinRT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread specs/MultiProfile.md Outdated
CoreWebView2ControllerOptions options);

Windows.Foundation.IAsyncOperation<CoreWebView2CompositionController>
CreateCoreWebView2CompositionControllerWithOptionsAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add [overload("CreateCoreWebView2CompositionController")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please add for .NET and WinRT

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread specs/MultiProfile.md Outdated
// ...

CoreWebView2ControllerOptions CreateCoreWebView2ControllerOptions(
String ProfileName, Boolean InPrivateModeEnabled);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

camelCase parameter names ("profileName" and "inPrivateModeEnabled"). (Same in methods below.)

Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These parameters were removed in a previous comment above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks.

Comment thread specs/MultiProfile.md Outdated
Comment thread specs/MultiProfile.md Outdated
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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profiles share some state but I'm not sure what state is shared. Please investigate and document.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add to the documentation some examples of content that is shared.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread specs/MultiProfile.md Outdated
/// 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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably that means that leading space is also disallowed. Also, I can't name a profile nul or com1.

Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. Thanks.

Comment thread specs/MultiProfile.md
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.");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the sample do its own profile name validation, rather than allowing invalid parameters to be passed through?

Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

@lichen63 lichen63 Sep 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment thread specs/MultiProfile.md Outdated
}

Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options;
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(
Copy link
Copy Markdown
Contributor

@oldnewthing oldnewthing Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I changed the interface to accept only ICoreWebView2ControllerOptions parameter. Please help check. Thanks.

Comment thread specs/MultiProfile.md
Comment thread specs/MultiProfile.md
String ProfilePath { get; };
}
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)?

Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will consider adding based on customer feedback.

Comment thread specs/MultiProfile.md
Comment thread specs/MultiProfile.md Outdated

Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options;
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(
m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. Document better how inPrivate interacts with profiles.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread specs/MultiProfile.md Outdated

Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options;
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(
m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread specs/MultiProfile.md Outdated
Comment thread specs/MultiProfile.md
Comment thread specs/MultiProfile.md Outdated

Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options;
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(
m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The profile is a subdirectory of the user data directory, and the app got to choose its user data directory location.

Comment thread specs/MultiProfile.md
m_profileDirName = str.substr(str.find_last_of(L'\\') + 1);

// update window title with m_profileDirName
UpdateAppTitle();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the sample could show the uninstall making one last profile so it can find and delete the content?

Comment thread specs/MultiProfile.md
interface ICoreWebView2Profile;

[uuid(C2669A3A-03A9-45E9-97EA-03CD55E5DC03), object, pointer_default(unique)]
interface ICoreWebView2ControllerOptions : IUnknown {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread specs/MultiProfile.md Outdated
Comment thread specs/MultiProfile.md Outdated
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.
Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Profiles share some state but I'm not sure what state is shared. Please investigate and document.

Comment thread specs/MultiProfile.md Outdated
Comment thread specs/MultiProfile.md Outdated
}

Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options;
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(
Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread specs/MultiProfile.md Outdated

Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options;
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(
m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. Document better how inPrivate interacts with profiles.

Comment thread specs/MultiProfile.md
Comment thread specs/MultiProfile.md Outdated
// ...

CoreWebView2ControllerOptions CreateCoreWebView2ControllerOptions(
String ProfileName, Boolean InPrivateModeEnabled);
Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These parameters were removed in a previous comment above.

Comment thread specs/MultiProfile.md Outdated
String ProfileName, Boolean InPrivateModeEnabled);

Windows.Foundation.IAsyncOperation<CoreWebView2Controller>
CreateCoreWebView2ControllerWithOptionsAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please add for .NET and WinRT

Comment thread specs/MultiProfile.md Outdated
CoreWebView2ControllerOptions options);

Windows.Foundation.IAsyncOperation<CoreWebView2CompositionController>
CreateCoreWebView2CompositionControllerWithOptionsAsync(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please add for .NET and WinRT

Comment thread specs/MultiProfile.md
String ProfilePath { get; };
}
}
```
Copy link
Copy Markdown
Contributor

@david-risney david-risney Sep 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will consider adding based on customer feedback.

Comment thread specs/MultiProfile.md Outdated
Comment thread specs/MultiProfile.md Outdated

Microsoft::WRL::ComPtr<ICoreWebView2ControllerOptions> options;
HRESULT hr = webViewEnvironment4->CreateCoreWebView2ControllerOptions(
m_webviewOption.profile.c_str(), m_webviewOption.isInPrivate, options.GetAddressOf());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread specs/MultiProfile.md
interface ICoreWebView2Profile;

[uuid(C2669A3A-03A9-45E9-97EA-03CD55E5DC03), object, pointer_default(unique)]
interface ICoreWebView2ControllerOptions : IUnknown {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@david-risney david-risney added the review completed WebView2 API Proposal that's been reviewed and now needs final update and push label Sep 28, 2021
@lichen63 lichen63 merged commit b6da5fb into master Oct 8, 2021
@lichen63 lichen63 deleted the api-multiprofile branch October 8, 2021 06:47
@lichen63 lichen63 restored the api-multiprofile branch October 18, 2021 02:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API Proposal Review WebView2 API Proposal for review. review completed WebView2 API Proposal that's been reviewed and now needs final update and push

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants