diff --git a/specs/ThrottlingControlScript.md b/specs/ThrottlingControlScript.md new file mode 100644 index 000000000..08b6fa1ec --- /dev/null +++ b/specs/ThrottlingControlScript.md @@ -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. + +```c# +void OnNoUserInteraction() +{ + // User is not interactive, keep webview visible but throttle timers to 500ms. + webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceForeground = 500; +} + +void OnUserInteraction() +{ + // User is interactive again, unthrottle foreground timers. + webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceForeground = 0; +} +``` + +```cpp +void ScenarioThrottlingControl::OnNoUserInteraction() +{ + wil::com_ptr settings; + m_webview->get_Settings(&settings); + auto settings2 = settings.try_query(); + CHECK_FEATURE_RETURN_EMPTY(settings2); + + // User is not interactive, keep webview visible but throttle timers to + // 500ms. + CHECK_FAILURE(settings2->put_ThrottlingIntervalPreferenceForeground(500)); +} + +void ScenarioThrottlingControl::OnUserInteraction() +{ + wil::com_ptr settings; + m_webview->get_Settings(&settings); + auto settings2 = settings.try_query(); + 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; + // Effectively disable intensive throttling by overriding its timer interval. + webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceIntensive = 0; + webView.Visibility = System.Windows.Visibility.Hidden; +} + +void DisableHiddenWebViewCore() +{ + webView.Visibility = System.Windows.Visibility.Visible; + webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceBackground = 1000; + webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceIntensive = 60000; +} +``` + +```cpp +void ScenarioThrottlingControl::SetupHiddenWebViewCore() +{ + wil::com_ptr settings; + m_webview->get_Settings(&settings); + auto settings2 = settings.try_query(); + 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 settings; + m_webview->get_Settings(&settings); + auto settings2 = settings.try_query(); + 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 +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; + } + }; + + webView.CoreWebView2.Settings.ThrottlingIntervalPreferenceIsolated = 500; +} +``` + +```cpp +void ScenarioThrottlingControl::SetupIsolatedFramesHandler() +{ + auto webview4 = m_webview.try_query(); + 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( + [this](ICoreWebView2* sender, ICoreWebView2FrameCreatedEventArgs* args) -> HRESULT + { + wil::com_ptr webviewFrame; + CHECK_FAILURE(args->get_Frame(&webviewFrame)); + + auto webviewFrame6 = + webviewFrame.try_query(); + 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 settings; + m_webview->get_Settings(&settings); + auto settings2 = settings.try_query(); + 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`. + /// + /// 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 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); + /// Sets the `ThrottlingIntervalPreferenceForeground` property. + [propput] HRESULT ThrottlingIntervalPreferenceForeground([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 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). + /// + /// 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 + /// `ShouldUseIsolatedThrottling` property is set to `TRUE`. This is a category + /// 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 + /// 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); +} + +``` + +```C# +namespace Microsoft.Web.WebView2.Core +{ + runtimeclass CoreWebView2 + { + // ... + + [interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2Settings9")] + { + UInt32 ThrottlingIntervalPreferenceForeground { get; set; }; + + 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