From bb119aba885511cb6c57c654c11a281cda5d5d74 Mon Sep 17 00:00:00 2001 From: Mike Wasserman Date: Fri, 26 Feb 2021 18:27:54 -0800 Subject: [PATCH] Window Placement: Implement Screens and ScreenAdvanced interfaces This CL makes the following changes for a planned API redesign: docs.google.com/document/d/1lgCentReLlym6j9kBS_Qo3r2HHZ6Ov7cSGjSWyi8cYs 1) Add Screens interface to vend multi-screen info for windowing. - RuntimeEnabled and permission-gated; designed like window.screen. 2) Add ScreenAdvanced interface, to extend Screen with extra info. - Make Screen impl non-final; override functions in ScreenAdvanced. 3) Add WindowScreens supplement, to host Window.getScreens(). - Rename the previous API access point to getScreensDeprecated(). 4) Add ScreenInfo::is_primary & is_internal to expose in ScreenAdvanced. - Add ScreenInfo::display_id for internal logic, it is not web-exposed. 5) Propagate multi-screen VisualProperties from browser to renderers: - Replace the singular ScreenInfo member with a new ScreenInfos struct. - Encapsulates multi-screen information and a current screen id. - Add [mutable_]current() for easy/legacy access to the current screen. - Validate mojo struct traits in [de]serialization; add tests. 6) Update WidgetBase, ChromeClient, tests, and more code accordingly. - Update RenderWidgetHostViewMac's cached display::Display on changes. - Add CoreInitializer plumbing to fire Screens.change in modules/. FOLLOWUP: Refine Screens::ScreenInfosChanged implementation. FOLLOWUP: Propagate multi-screen info to RemoteFrames. FOLLOWUP: Update/add tests; remove old API and plumbing. FOLLOWUP: Add WindowScreens PermissionObserver & set_disconnect_handler? FOLLOWUP: Use WebContentsImpl's NativeView in RWHI::GetScreenInfo? Bug: 897300, 1116528, 1138596, 1169312, 1116528, 1179876, 1179945 Test: New API roughly WAI w/ --enable-blink-features=WindowPlacement Change-Id: I1d67cfabda62796274992e3e650d5209dd7bb857 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2669359 Commit-Queue: Michael Wasserman Reviewed-by: John Abd-El-Malek Reviewed-by: danakj Reviewed-by: Daniel Cheng Reviewed-by: Victor Costan Cr-Commit-Position: refs/heads/master@{#858379} --- .../getScreens.tentative.https.window.js | 31 ++++++++++++++----- .../getScreens.values.https.html | 22 ++++++------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/screen_enumeration/getScreens.tentative.https.window.js b/screen_enumeration/getScreens.tentative.https.window.js index acd38bb03b20cb..a4d6ec0465a48c 100644 --- a/screen_enumeration/getScreens.tentative.https.window.js +++ b/screen_enumeration/getScreens.tentative.https.window.js @@ -9,8 +9,10 @@ promise_test(async t => { promise_test(async t => { await test_driver.set_permission({name: 'window-placement'}, 'granted'); - const screens = await self.getScreens(); + const screensInterface = await self.getScreens(); + const screens = screensInterface.screens; assert_greater_than(screens.length, 0); + assert_true(screens.includes(screensInterface.currentScreen)); assert_equals(typeof screens[0].availWidth, 'number'); assert_equals(typeof screens[0].availHeight, 'number'); @@ -25,16 +27,18 @@ promise_test(async t => { assert_equals(typeof screens[0].top, 'number'); assert_equals(typeof screens[0].orientation, 'object'); - assert_equals(typeof screens[0].primary, 'boolean'); - assert_equals(typeof screens[0].internal, 'boolean'); - assert_equals(typeof screens[0].scaleFactor, 'number'); + assert_equals(typeof screens[0].isExtended, 'boolean'); + assert_equals(typeof screens[0].isPrimary, 'boolean'); + assert_equals(typeof screens[0].isInternal, 'boolean'); + assert_equals(typeof screens[0].devicePixelRatio, 'number'); assert_equals(typeof screens[0].id, 'string'); - assert_equals(typeof screens[0].touchSupport, 'boolean'); + assert_equals(typeof screens[0].pointerTypes, 'object'); + assert_equals(typeof screens[0].label, 'string'); }, 'getScreens() returns at least 1 Screen with permission granted'); promise_test(async t => { await test_driver.set_permission({name: 'window-placement'}, 'granted'); - assert_greater_than((await self.getScreens()).length, 0); + assert_greater_than((await self.getScreens()).screens.length, 0); await test_driver.set_permission({name: 'window-placement'}, 'denied'); await promise_rejects_dom(t, 'NotAllowedError', self.getScreens()); }, 'getScreens() rejects the promise with permission denied'); @@ -42,7 +46,7 @@ promise_test(async t => { promise_test(async t => { await test_driver.set_permission({name: 'window-placement'}, 'granted'); let iframe = document.body.appendChild(document.createElement('iframe')); - assert_greater_than((await iframe.contentWindow.getScreens()).length, 0); + assert_greater_than((await iframe.contentWindow.getScreens()).screens.length, 0); let iframeGetScreens; let constructor; @@ -62,3 +66,16 @@ promise_test(async t => { assert_equals(iframe.contentWindow, null); await promise_rejects_dom(t, 'InvalidStateError', constructor, iframeGetScreens()); }, "getScreens() resolves for attached iframe; rejects for detached iframe"); + +promise_test(async t => { + await test_driver.set_permission({name: 'window-placement'}, 'granted'); + let iframe = document.body.appendChild(document.createElement('iframe')); + const screensInterface = await iframe.contentWindow.getScreens(); + assert_greater_than(screensInterface.screens.length, 0); + assert_equals(screensInterface.currentScreen, screensInterface.screens[0]); + iframe.remove(); + await t.step_wait(() => !iframe.contentWindow, "execution context invalid"); + assert_equals(iframe.contentWindow, null); + assert_equals(screensInterface.screens.length, 0); + assert_equals(screensInterface.currentScreen, null); +}, "Cached Screens interface from detached iframe doesn't crash, behaves okay"); diff --git a/screen_enumeration/getScreens.values.https.html b/screen_enumeration/getScreens.values.https.html index 93b27b465561c1..b29ff5ab309d59 100644 --- a/screen_enumeration/getScreens.values.https.html +++ b/screen_enumeration/getScreens.values.https.html @@ -1,6 +1,6 @@ -Window Placement: getScreens() tentative +Window Placement: getScreensDeprecated() tentative @@ -25,8 +25,8 @@ screen_enumeration_test(async (t, mockScreenEnum) => { mockScreenEnum.setSuccess(true); await test_driver.set_permission({name: 'window-placement'}, 'granted'); - assert_equals((await self.getScreens()).length, 0); -}, 'getScreens() supports an empty set of mocked screens'); + assert_equals((await self.getScreensDeprecated()).length, 0); +}, 'getScreensDeprecated() supports an empty set of mocked screens'); screen_enumeration_test(async (t, mockScreenEnum) => { let display1 = makeDisplay(10, @@ -41,13 +41,13 @@ await test_driver.set_permission({name: 'window-placement'}, 'granted'); - const screens = await self.getScreens(); + const screens = await self.getScreensDeprecated(); assert_equals(screens.length, 1); check_screen_matches_display(screens[0], display1); assert_equals(screens[0].primary, true); assert_equals(screens[0].internal, true); assert_equals(screens[0].id, '0'); -}, 'getScreens() supports a single mocked screen'); +}, 'getScreensDeprecated() supports a single mocked screen'); screen_enumeration_test(async (t, mockScreenEnum) => { let display1 = makeDisplay(10, @@ -74,7 +74,7 @@ await test_driver.set_permission({name: 'window-placement'}, 'granted'); - let screens = await self.getScreens(); + let screens = await self.getScreensDeprecated(); assert_equals(screens.length, 3); check_screen_matches_display(screens[0], display1); assert_equals(screens[0].primary, true); @@ -90,7 +90,7 @@ assert_equals(screens[2].id, '2'); mockScreenEnum.removeDisplay(display2.id); - screens = await self.getScreens(); + screens = await self.getScreensDeprecated(); assert_equals(screens.length, 2); check_screen_matches_display(screens[0], display1); assert_equals(screens[0].id, '0'); @@ -98,16 +98,16 @@ assert_equals(screens[1].id, '1'); mockScreenEnum.removeDisplay(display1.id); - screens = await self.getScreens(); + screens = await self.getScreensDeprecated(); assert_equals(screens.length, 1); check_screen_matches_display(screens[0], display3); assert_equals(screens[0].id, '0'); -}, 'getScreens() supports multiple mocked screens'); +}, 'getScreensDeprecated() supports multiple mocked screens'); screen_enumeration_test(async (t, mockScreenEnum) => { mockScreenEnum.setSuccess(false); await test_driver.set_permission({name: 'window-placement'}, 'granted'); - promise_rejects_dom(t, 'NotAllowedError', self.getScreens()); -}, 'getScreens() rejects when the mock success value is set to false'); + promise_rejects_dom(t, 'NotAllowedError', self.getScreensDeprecated()); +}, 'getScreensDeprecated() rejects when the mock success value is set false');