Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spec: Throttling Control - Script #4156

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
325 changes: 325 additions & 0 deletions specs/ThrottlingControlScript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
Throttling Control - Script Throttling
===

# Background
Web content in WebView2 is generally subject to the same Web Platform
restrictions as in the Microsoft Edge browser. However, some of the scenarios
for WebView2 applications differ from the scenarios in the browser. For this
reason, we're providing a set of APIs to fine-tune performance of scripts
running in WebView2. These APIs allow WebView2 applications to achieve two
things:

* Customize script timers (`setTimeout` and `setInterval`) throttling under
different page states (foreground, background, and background with intensive
throttling)
* Throttle script timers in select hosted iframes

# Examples

## Throttle timers in visible WebView

Throttling Control APIs allow you to throttle JavaScript timers in scenarios
where the WebView2 control in your application needs to remain visible, but
consume less resources, for example, when the user is not interactive.
Copy link

Choose a reason for hiding this comment

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

It might be helpful to elaborate a bit on what 'throttling' means in this case. If I set a throttling interval of 500ms, that means that timers will file at most once every 500ms - is that correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

Especially because the Chromium docs call out two stages of throttling.

Stage 1: Timers run at normal speed for X seconds.
Stage 2: After X seconds, timers run at maximum speed Y.

It may not be clear whether this setting controls X or Y. I think it controls Y.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This paragraph is for API review only. Proper documentation for "throttling" meaning is in the API:

/// The preferred wake up interval (in milliseconds) to use for throttleable
/// JavaScript tasks (`setTimeout` and `setInterval`) [...]

/// A wake up interval is the amount of time that needs to pass before the
/// WebView2 Runtime checks for new timer tasks to run.

/// [...]
[propget] HRESULT ThrottlingIntervalPreferenceForeground([out, retval] UINT32* value);

I can use similar wording to the question above in the "For example" portion of the docs:

If I set a throttling interval of 500ms, that means that timers will file at most once every 500ms

/// For example, an application might use a foreground value of 30 ms for
/// moderate throttling scenarios, or match the default background value
/// (usually 1000 ms). **In this case, timers will file at most once every 30ms.**

Copy link
Contributor Author

@lflores-ms lflores-ms Nov 21, 2023

Choose a reason for hiding this comment

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

The two stages of throttling from the referred document map as follows:

  • X -> ThrottlingIntervalPreferenceForeground
  • Y -> ThrottlingIntervalPreferenceBackground
  • there's a third state: ThrottlingIntervalPreferenceIntensive

Copy link
Contributor

Choose a reason for hiding this comment

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

Please update docs. See discussion later on for changing name to be clearer.


```c#
void OnNoUserInteraction()
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like an event callback, but it is implied business logic of the sample app, not something rooted in the wv2 API surface, correct? Is there a prescribed way app should determine when the customer isn't "interactive"? (Or, is this concept already present in our samples elsewhere?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No prescribed way to implement detection logic, this is up to the app.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a comment to make it clear how the app would call this method.
A comment like 'the sample app calls this when ... but you might consider doing this in these cases ...'.
Follow up if we want to improve sample code to demonstrate how to actually do this - if we determine this is a common thing that will be helpful.

{
// User is not interactive, keep webview visible but throttle timers to 500ms.
webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceForeground = 500;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have precedence for wv2 intervals implicitly being milliseconds? I was going to propose including this in the property names, but realized we're aligning to JS' global functions that already do this. I didn't quickly find an existing webview2-related milliseconds property to check.

(Closest was bufferUsageReportingInterval, so maybe this is OK?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See comment above on "Milliseconds": #4156 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

See that comment for resolution.

Copy link
Contributor

Choose a reason for hiding this comment

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

There are multiple types of throttling in Chromium. Timer throttling. Network throttling. Tab throttling. Extension throttling. CPU throttling. Authentication throttling.

From its name, it is not clear which of the many types of throttling this setting controls.

TimerThrottlingPreference(Foreground|Background|Intensive|Isolated)?

Or, for grammar,

(Foreground|Background|Intensive|Isolated)TimerThrottlingInterval?

Or if we really need to emphasize that this is a preference that may not be honored

Preferred(Foreground|Background|Intensive|Isolated)TimerThrottlingInterval?

Even the term "interval" is ambiguous. Is this the interval between throttlings? (No, it's the interval of the timer.)

Other parts of the documentation call this the "wake-up" interval.

Preferred(Foreground|Background|Intensive|Isolated)TimerWakeInterval?

Copy link
Contributor

Choose a reason for hiding this comment

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

Please use Preferred(Foreground|Background|Intensive|Isolated)TimerWakeInterval(InMilliseconds) (InMilliseconds only for COM)

Copy link
Contributor

Choose a reason for hiding this comment

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

Note that the term "Foreground" here does not refer to Win32 "foreground" (receives keyboard input) but rather the Chromium concept of "foreground" which just means "programmatically visible". And even a programmatically visible that is completely occluded (so not visible to the end user) counts as "foreground".

Is this web-centric definition of "foreground" already established in WebView2-land?

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a different kind of foreground defined in MDN: https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API.

Please include definition and link in our docs.

}

void OnUserInteraction()
{
// User is interactive again, unthrottle foreground timers.
webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceForeground = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Reading this sample, I though it was reverting OnNoUserInteraction, but it's actually putting it into a third mode.

Copy link
Contributor

Choose a reason for hiding this comment

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

Update comment to make it clear if we're going to default value or not. (Confusion if this is supposed to be default value or 0). And consider updating sample code to show how to go to default value.

}
```

```cpp
void ScenarioThrottlingControl::OnNoUserInteraction()
{
wil::com_ptr<ICoreWebView2Settings> settings;
m_webview->get_Settings(&settings);
auto settings2 = settings.try_query<ICoreWebView2Settings9>();
CHECK_FEATURE_RETURN_EMPTY(settings2);

// User is not interactive, keep webview visible but throttle timers to
// 500ms.
CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceForeground(500));
Copy link
Contributor

Choose a reason for hiding this comment

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

When do these changes take effect? According to the documentation, all Settings properties take effect at the next top-level navigation, with two explicit exceptions that take effect immediately.

Copy link
Contributor Author

@lflores-ms lflores-ms Nov 21, 2023

Choose a reason for hiding this comment

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

Takes effect immediately. Will update the docs to state that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please make explicit in our documentation.

}

void ScenarioThrottlingControl::OnUserInteraction()
{
wil::com_ptr<ICoreWebView2Settings> settings;
m_webview->get_Settings(&settings);
auto settings2 = settings.try_query<ICoreWebView2Settings9>();
CHECK_FEATURE_RETURN_EMPTY(settings2);

// User is interactive again, unthrottle foreground timers.
CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceForeground(0));
}
```

## Unthrottle timers in hidden WebView

Throttling Control APIs allow you to set a custom throttling interval for timers
on hidden WebViews. For example, if there's logic in your app that runs in
JavaScript but doesn't need to render content, you can keep the WebView hidden
and unthrottle its timers.

```C#
void SetupHiddenWebViewCore()
{
// This WebView2 will remain hidden but needs to keep running timers.
// Unthrottle background timers.
webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceBackground = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Include declarations of webView or comments to make it clear what the type of webView is (confusion over CoreWebView2, WebView2, CoreWebView2Controller).

Or add a member coreWebView2_ so we don't have to deal with UI framework elements.

// Effectively disable intensive throttling by overriding its timer interval.
webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceIntensive = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes.

webView.Visibility = System.Windows.Visibility.Hidden;
Copy link
Contributor

Choose a reason for hiding this comment

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

If we want the throttling changes to take effect immediately, do we need to do a fake "navigate to self"? (Though that would destroy any page state, so maybe not?)

Copy link
Contributor

Choose a reason for hiding this comment

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

n/a see above

Copy link

@sschalek sschalek Nov 21, 2023

Choose a reason for hiding this comment

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

Does the hosted JS know that it is invisible but there is a desire for it to still run in this state? Or would this info have to come through a different channel, or is the expectation that hosted code will always try to run if it can and won't itself, say, stop regular updates or communication with its backing service if the web platform says it's not visible?

Another way of putting it is, how is "visible as far as the web platform is concerned" differentiated from "host app has set the WV2 element to be visible" from "content is actually visible" both from the perspective of the host app calling these APIs and the hosted code having access to the web platform APIs?

Copy link
Contributor Author

@lflores-ms lflores-ms Nov 21, 2023

Choose a reason for hiding this comment

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

JavaScript context only has knowledge of "visible as far as the web platform is concerned" through Page Visibility API.

From the host app side, this is controlled by CoreWebView2Controller.IsVisible property. For WebView2 controls in .NET/WinRT, IsVisible property is controlled directly by the framework and tied to actual user visibility. The hosted JavaScript has no knowledge of this through its Web Platform API, but if needed, can be informed by the host through window.chrome.webview object.

Copy link
Contributor

Choose a reason for hiding this comment

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

Consider adding explicit end to end description of how API fits into chromium/web platform feature to the ref docs and examples of how an end dev might use it

}

void DisableHiddenWebViewCore()
Copy link
Contributor

Choose a reason for hiding this comment

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

This method name is confusing. The name says that it's disabling the hidden webview, but really it's showing the webview and setting new values.

Is this code trying to restore defaults? If so, it should save the previous values of the properties in SetupHiddenWebViewCore so that it can restore them here, rather than hard-coding what it believes to be the defaults.

Copy link
Contributor

Choose a reason for hiding this comment

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

Name is sort of double negative. Better would be Hide/ShowWebView
If we're trying to undo previous changes back to previous values, we should be saving those values or showing how to do this properly.

{
webView.Visibility = System.Windows.Visibility.Visible;
webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceBackground = 1000;
webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceIntensive = 60000;
}
```

```cpp
void ScenarioThrottlingControl::SetupHiddenWebViewCore()
{
wil::com_ptr<ICoreWebView2Settings> settings;
m_webview->get_Settings(&settings);
auto settings2 = settings.try_query<ICoreWebView2Settings9>();
CHECK_FEATURE_RETURN_EMPTY(settings2);

// This WebView2 will remain hidden but needs to keep running timers.
// Unthrottle background timers.
CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceBackground(0));
// Effectively disable intensive throttling by overriding its timer interval.
CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceIntensive(0));

CHECK_FAILURE(m_appWindow->GetWebViewController()->put_IsVisible(FALSE));
}

void ScenarioThrottlingControl::DisableHiddenWebViewCore()
{
CHECK_FAILURE(m_appWindow->GetWebViewController()->put_IsVisible(TRUE));

wil::com_ptr<ICoreWebView2Settings> settings;
m_webview->get_Settings(&settings);
auto settings2 = settings.try_query<ICoreWebView2Settings9>();
CHECK_FEATURE_RETURN_EMPTY(settings2);

CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceBackground(1000));
CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceIntensive(60000));
}
```

## Throttle timers in hosted iframes

Throttling Control APIs allow you to throttle timers in specific frames within
Copy link

Choose a reason for hiding this comment

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

So every frame is either a 'regular' frame or an 'isolated' frame. Regular frames are controlled by ThrottlingIntervalPreferenceForeground/ThrottlingIntervalPreferenceBackground/ThrottlingIntervalPreferenceIntensive. Isolated frames are controlled by ThrottlingIntervalPreferenceIsolated.

Is there a reason why an isolated frame does not have Foreground/Background/Intensive properties? Or is that not an interesting scenario?

Copy link
Contributor

Choose a reason for hiding this comment

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

Valid question - Mostly not an interesting enough scenario to add the complexity. The primary use-case here is an app embedding 3rd party content and wanting to be able to independently limit the performance impact of it. Generally that's something like "low battery, throttle more" or "giving the frame N seconds to run some logic, throttle less".

The case where they'd want to put it in an isolated group while still managing foreground/background/intensive independently of the non-isolated timers would be a niche of a niche that even our most micromanage-y apps wouldn't want that granularity.

Copy link
Contributor

Choose a reason for hiding this comment

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

So this value takes precedence over the other values?

Copy link
Contributor

Choose a reason for hiding this comment

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

Update sample to set isolated to at least match background to help demonstrate intended scenario / usage of isolated. Use Andy's comment to update the sample code.

Other names to consider:

  • Custom
  • Alternate
  • Override

Use the following:
PreferredOverrideTimerWakeIntervalInMilliseconds
UseOverrideTimerWakeInterval

the WebView2 control. For example, if your application uses iframes to host 3rd
party content, you can select and mark these frames to be throttled separately
from the main frame and other regular, unmarked frames.

```C#
void SetupIsolatedFramesHandler()
{
// You can use the frame properties to determine whether it should be
// marked to be throttled separately from main frame.
webView.CoreWebView2.FrameCreated += (sender, args) =>
{
if (args.Frame.Name == "isolated")
{
args.Frame.ShouldUseIsolatedThrottling = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

When does this setting take effect? Immediately? On next frame navigation? Is setting it in FrameCreated "soon enough"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Takes effect immediately. FrameCreated is the first chance to set the property as this is when we get the CoreWebView2Frame object.

Copy link
Contributor

Choose a reason for hiding this comment

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

see above

Copy link
Contributor

Choose a reason for hiding this comment

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

Does this affect nested frames? Or does the code need to handle hierarchy in the event handler?

Copy link
Contributor

Choose a reason for hiding this comment

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

Great question!

Please update ref docs to clearly state it.

Please follow up with PM if we need this to be inherited or not. Alternatively, if grandchild frame event is coming soon then this is less interesting question to answer and not inherit is easier.

}
};

webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceIsolated = 500;
}
```

```cpp
void ScenarioThrottlingControl::SetupIsolatedFramesHandler()
{
auto webview4 = m_webview.try_query<ICoreWebView2_4>();
CHECK_FEATURE_RETURN_EMPTY(webview4);

// You can use the frame properties to determine whether it should be
// marked to be throttled separately from main frame.
CHECK_FAILURE(webview4->add_FrameCreated(
Callback<ICoreWebView2FrameCreatedEventHandler>(
[this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT
{
wil::com_ptr<ICoreWebView2Frame> webviewFrame;
CHECK_FAILURE(args->get_Frame(&webviewFrame));

auto webviewFrame6 =
webviewFrame.try_query<ICoreWebView2Frame6>();
CHECK_FEATURE_RETURN_HRESULT(webviewFrame6);

wil::unique_cotaskmem_string name;
CHECK_FAILURE(webviewFrame->get_Name(&name));
if (wcscmp(name.get(), L"isolated") == 0)
{
CHECK_FAILURE(webviewFrame6->put_ShouldUseIsolatedThrottling(TRUE));
}

return S_OK;
})
.Get(),
&m_frameCreatedToken));

wil::com_ptr<ICoreWebView2Settings> settings;
m_webview->get_Settings(&settings);
auto settings2 = settings.try_query<ICoreWebView2Settings9>();
CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceIsolated(500));
}
```

# API Details
```cpp
/// A continuation of the `ICoreWebView2Settings` interface to support
/// ThrottlingPreference.
[uuid(00f1b5fb-91ed-4722-9404-e0f8fd1e6b0a), object, pointer_default(unique)]
interface ICoreWebView2Settings9 : ICoreWebView2Settings8 {
/// The preferred wake up interval (in milliseconds) to use for throttleable
/// JavaScript tasks (`setTimeout` and `setInterval`), when the WebView is in
/// foreground state. A WebView is in foreground state when its `IsVisible`
/// property is `TRUE`.
Comment on lines +191 to +192
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this name mapping "visible" to "foreground" standard terminology? Otherwise, calling this ThrottlingIntervalPreferenceWhenVisible would be easier to understand

Also PreferredThrottlingInterval... rather than ThrottlingIntervalPreference....

Copy link
Contributor

Choose a reason for hiding this comment

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

See above.

///
/// A wake up interval is the amount of time that needs to pass before the
/// WebView2 Runtime checks for new timer tasks to run.
///
/// The WebView2 Runtime will try to respect the preferred interval set by the
/// application, but the effective value will be constrained by resource and
/// platform limitations. Setting a value of `0` means a preference of no
/// throttling to be applied. The default value is a constant determined by
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we're missing a way to return to the WebView2 Runtime's default value. (In other words, there's no input for "I don't have a preference" for any of these properties?) I might expect preference=0 means I don't have any preference, but instead it means I prefer zero throttling.

Best case, the constant determined by the running version of the WebView2 Runtime is fixed, and my app can save it before overriding if I'd care about restoring it to 'default'?

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// throttling to be applied. The default value is a constant determined by
/// throttling to be applied. The default value is determined by

I would simplify the text to say that the default value is determined by the runtime. Don't over-promise that it's a constant. It might be determined dynamically (one value if on AC, another if on battery, and another if in energy saver mode).

It seems that the only way to restore default behavior is to save the old value and set it back, and hope that there is no dynamic throttling.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is a known limitation and the only solution in current design is to save the value before changes. The intention behind this was to keep the API simpler, but if this is semantically meaningful, I can think of two options:

option 1

  • add toggle - e.g., CoreWebView2Settings.IsThrottlingEnabled
    • under current pattern, it would be 4 additional properties
    • alternatively, we can bring back the "throttling category" enum and use for both interval and toggle
  • make 0 reset to default

option 2

  • make 0 reset to default
  • make 1-4 mean off
    • due to architectural reasons, there's always an effective minimum 4 ms throttling (even when throttling is off)

none (current)

  • save value before changing, then restore

... or a reversal of option 1? (method for reset and keep 0 meaning off) I think option 1 is better than its reversal.

Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer the 'simpler' option: save value before changing then restore - no new API changes.

/// the running version of the WebView2 Runtime.
///
/// For example, an application might use a foreground value of 30 ms for
/// moderate throttling scenarios, or match the default background value
/// (usually 1000 ms).
[propget] HRESULT ThrottlingIntervalPreferenceForeground([out, retval] UINT32* value);
Copy link
Contributor

Choose a reason for hiding this comment

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

Typing guideline is Int32, unless it's truly necessary to support >2 billion

Copy link
Contributor

Choose a reason for hiding this comment

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

Please fix.

/// Sets the `ThrottlingIntervalPreferenceForeground` property.
[propput] HRESULT ThrottlingIntervalPreferenceForeground([in] UINT32 value);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Naming of these properties should follow pattern of existing WebView2 APIs, using "Preferred" as prefix:

  • PreferredColorScheme
  • PreferredTrackingPreventionLevel

Copy link
Contributor

Choose a reason for hiding this comment

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

See above!


/// The preferred wake up interval (in milliseconds) to use for throttleable
/// JavaScript tasks (`setTimeout` and `setInterval`), when the WebView is in
/// background state, with no intensive throttling. A WebView is in background
/// state when its `IsVisible` property is `FALSE`. Intensive throttling is a
/// substate of background state. For more details about intensive throttling,
/// see [Intensive throttling of Javascript timer wake ups](https://chromestatus.com/feature/4718288976216064).
///
/// A wake up interval is the amount of time that needs to pass before the
/// WebView2 Runtime checks for new timer tasks to run.
///
/// The WebView2 Runtime will try to respect the preferred interval set by the
/// application, but the effective value will be constrained by resource and
/// platform limitations. Setting a value of `0` means a preference of no
/// throttling to be applied. The default value is a constant determined by
/// the running version of the WebView2 Runtime. All other background state
/// policies (including intensive throttling) are effective independently of
/// this setting.
///
/// For example, an application might use a background value of 100 ms to
/// relax the default background value (usually 1000 ms).
[propget] HRESULT ThrottlingIntervalPreferenceBackground([out, retval] UINT32* value);
/// Sets the `ThrottlingIntervalPreferenceBackground` property.
[propput] HRESULT ThrottlingIntervalPreferenceBackground([in] UINT32 value);

/// The preferred wake up interval (in milliseconds) to use for throttleable
/// JavaScript tasks (`setTimeout` and `setInterval`), when the WebView is in
/// background state with intensive throttling. Intensive throttling is a
/// substate of background state. For more details about intensive
/// throttling, see
/// [Intensive throttling of Javascript timer wake ups](https://chromestatus.com/feature/4718288976216064).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// [Intensive throttling of Javascript timer wake ups](https://chromestatus.com/feature/4718288976216064).
/// [Intensive throttling of JavaScript timer wake ups](https://chromestatus.com/feature/4718288976216064).

nit, capitalization on this instance of JavaScript

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is from the title of the linked document, so I'd lean towards keeping current casing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Leave as is.

///
/// A wake up interval is the amount of time that needs to pass before the
/// WebView2 Runtime checks for new timer tasks to run.
///
/// The WebView2 Runtime will try to respect the preferred interval set by the
/// application, but the effective value will be constrained by resource and
/// platform limitations. Setting a value of `0` means a preference of no
/// throttling to be applied. The default value is a constant determined by
/// the running version of the WebView2 Runtime.
[propget] HRESULT ThrottlingIntervalPreferenceIntensive([out, retval] UINT32* value);
/// Sets the `ThrottlingIntervalPreferenceIntensive` property.
[propput] HRESULT ThrottlingIntervalPreferenceIntensive([in] UINT32 value);

/// The preferred wake up interval (in milliseconds) to use for throttleable
/// JavaScript tasks (`setTimeout` and `setInterval`), in frames whose
Copy link
Contributor

Choose a reason for hiding this comment

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

This Interval Preference is unique among the four, in that it applies to frame hosted content instead of this WebView's direct content. Maybe worth differentiating e.g. ThrottlingIntervalPreferenceIsolatedFrames

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For review discussion
I agree and intent to update the name unless there's a different outcome during review.

Copy link
Contributor

Choose a reason for hiding this comment

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

See above.

Copy link
Contributor

Choose a reason for hiding this comment

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

All isolated Frames are subject to the same throttling preference? (I infer there's just little value in being able to throttle isolated frames to different intervals. Is that right?)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, please make explicit in ref docs.

/// `ShouldUseIsolatedThrottling` property is set to `TRUE`. This is a category
Copy link
Contributor

Choose a reason for hiding this comment

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

An alternate design appears to be one uint32 property on CoreWebView2Frame, instead of a pair uint32 + Boolean properties to set the interval value and opt-in to applying it. Fewer properties to manage are often better. Was it discussed whether grouping this near the other IntervalPreferences is higher value in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alternative design options are limited by the Chromium architecture for throttling. While it's likely technically feasible to override individual frame intervals, a change like this would mean significantly more implementation complexity and risk. This was discussed during original design of the API, but we didn't identify a strong need for it, so we landed on a compromise of "isolated" interval + per-frame opt in.

If you have a scenario that strongly requires per-frame interval, please feel free to share in this review, or reach out to us through email.

Copy link
Contributor

Choose a reason for hiding this comment

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

Existing API surface better matches intended scenario. Leave as is.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add for emphasis

in frames whose ShouldUseIsolatedThrottling property is set to TRUE, regardless of whether they are foreground or background.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please update.

/// specific to WebView2 with no corresponding state in the Chromium tab state
/// model.
///
/// A wake up interval is the amount of time that needs to pass before the
/// WebView2 Runtime checks for new timer tasks to run.
///
/// The WebView2 Runtime will try to respect the preferred interval set by the
/// application, but the effective value will be constrained by resource and
/// platform limitations. Setting a value of `0` means a preference of no
/// throttling to be applied. The default value is a constant determined by
/// the running version of the WebView2 Runtime.
///
/// For example, an application might use an isolated throttling value of 30
/// ms to reduce resource consumption from third party frames in the WebView.
[propget] HRESULT ThrottlingIntervalPreferenceIsolated([out, retval] UINT32* value);
/// Sets the `ThrottlingIntervalPreferenceIsolated` property.
[propput] HRESULT ThrottlingIntervalPreferenceIsolated([in] UINT32 value);
}

/// A continuation of the `ICoreWebView2Frame` interface to support
/// ShouldUseIsolatedThrottling property.
[uuid(5b7d1b96-699b-44a2-b9f1-b8e88f9ac2be), object, pointer_default(unique)]
interface ICoreWebView2Frame6 : ICoreWebView2Frame5 {
/// Indicates whether the frame has been marked for isolated throttling by the
/// host app. When `TRUE`, the frame will receive the throttling interval set
Copy link
Contributor

Choose a reason for hiding this comment

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

"Should" suggests application preference. Are there any cases where the Boolean won't match whether IntervalPreferenceIsolated is being used? Documentation suggests this is fully IsUsingThrottlingIntervalPreferenceIsolated. (or a shorter name that is as definite.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Rename Should -> UseOverrideTimerWakeInterval

/// by `ThrottlingIntervalPreferenceIsolated`. When `FALSE`, and for main
/// frame, throttling interval will be determined by page state and the
/// interval through their corresponding properties in the
/// `CoreWebView2Settings` object. Defaults to `FALSE` unless set otherwise.
[propget] HRESULT ShouldUseIsolatedThrottling([out, retval] BOOL* value);

/// Sets the `ShouldUseIsolatedThrottling` property.
[propput] HRESULT ShouldUseIsolatedThrottling([in] BOOL value);
Copy link
Contributor

Choose a reason for hiding this comment

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

If we add other types of throttling (network, etc.), will this flag also opt into isolated network throttling etc? Or is this only for timer throttling?

Copy link
Contributor

Choose a reason for hiding this comment

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

New name fixes this. See above.

}

```

```C#
namespace Microsoft.Web.WebView2.Core
{
runtimeclass CoreWebView2
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note: class here should be CoreWebView2Settings, matching the COM API above.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please fix!

{
// ...

[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2Settings9")]
{
UInt32 ThrottlingIntervalPreferenceForeground { get; set; };
Copy link

Choose a reason for hiding this comment

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

If these are all values in milliseconds, we should communicate that through the api. Either by the name of the api or by changing the type from UInt32 to something like TimeSpan.

Since you also ship a pure COM api in addition to the WinRT api, the TimeSpan type might not be available (I'm not sure).

Consider a name such as:

ThrottlingIntervalPreferenceInMillisecondsForeground 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For review discussion
I'm not aware of an available COM TimeSpan-equivalent type. I can think of the following options:

  • Keep current name pattern. There's likely precedent in CoreWebView2Notification.TimeStamp. Other TimeStamp APIs in Webview2 do put units in docs rather than the name: CoreWebView2Texture.TimeStamp (microseconds)
  • Use "Milliseconds" in the name. For discussion -- which position and whether to use "Milliseconds" or "InMilliseconds": PreferredThrottlingInterval<1>Foreground<2>

In either case, we can keep UInt32 in COM and expose as TimeSpan in .NET/WinRT.

Copy link
Contributor

Choose a reason for hiding this comment

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

InMilliseconds as a suffix

Copy link
Contributor

Choose a reason for hiding this comment

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

COM: UInt32 InMilliseconds
.NET/WinRT TimeSpan no explicit suffix


UInt32 ThrottlingIntervalPreferenceBackground { get; set; };

UInt32 ThrottlingIntervalPreferenceIntensive { get; set; };

UInt32 ThrottlingIntervalPreferenceIsolated { get; set; };
}
}

runtimeclass CoreWebView2Frame
{
// ...

[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2Frame6")]
{
Boolean ShouldUseIsolatedThrottling { get; set; };
}
}
}

```

# Appendix