Skip to content
Draft
Show file tree
Hide file tree
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
61 changes: 61 additions & 0 deletions css/cssom-view/devicepixelratio-override-bidi.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!doctype html>
<meta charset="utf-8">
<title>devicePixelRatio reflects BiDi setViewport override</title>
<link rel="help" href="https://www.w3.org/TR/cssom-view/#dom-window-devicepixelratio">
<link rel="help" href="https://www.w3.org/TR/webdriver-bidi/#command-browsingContext-setViewport">
<link rel="help" href="https://www.w3.org/TR/mediaqueries-4/#resolution">

<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js?feature=bidi"></script>
<script src="/resources/testdriver-vendor.js"></script>

<script>
function getTargetDPR() {
// Pick a value that's different from the current DPR, so the test
// still detects failures even if the default environment is already 2.
return window.devicePixelRatio === 2 ? 1 : 2;
}

function getNonMatchingDPR(target) {
return target === 2 ? 1 : 2;
}

function waitForNextAnimationFrame() {
return new Promise(resolve => requestAnimationFrame(resolve));
}

async function setDevicePixelRatio(value) {
await test_driver.bidi.browsing_context.set_viewport({
context: window,
devicePixelRatio: value,
});
await waitForNextAnimationFrame();
}

promise_test(async t => {
const targetDPR = getTargetDPR();
t.add_cleanup(() => setDevicePixelRatio(null));
await setDevicePixelRatio(targetDPR);
assert_equals(
window.devicePixelRatio,
targetDPR,
"window.devicePixelRatio should reflect the BiDi override"
);
}, "window.devicePixelRatio reflects a BiDi devicePixelRatio override");

promise_test(async t => {
const targetDPR = getTargetDPR();
const nonMatchingDPR = getNonMatchingDPR(targetDPR);
t.add_cleanup(() => setDevicePixelRatio(null));
await setDevicePixelRatio(targetDPR);
assert_true(
matchMedia(`(resolution: ${targetDPR}dppx)`).matches,
"The matching resolution media query should match after the override"
);
assert_false(
matchMedia(`(resolution: ${nonMatchingDPR}dppx)`).matches,
"A different resolution media query should not match after the override"
);
}, "resolution media queries reflect a BiDi devicePixelRatio override");
</script>
42 changes: 42 additions & 0 deletions resources/testdriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,42 @@
},
}
},
/**
* `browsingContext <https://www.w3.org/TR/webdriver-bidi/#module-browsingContext>`_ module.
*/
browsing_context: {
/**
* Sets the viewport and/or device pixel ratio override for the
* specified browsing context. Matches the
* `browsingContext.setViewport
* <https://www.w3.org/TR/webdriver-bidi/#command-browsingContext-setViewport>`_
* WebDriver BiDi command.
*
* @example
* await test_driver.bidi.browsing_context.set_viewport({
* devicePixelRatio: 2
* });
*
* @param {object} params - Parameters for the command.
* @param {Context} [params.context] The optional browsing
* context to update. If omitted and `params.userContexts` is
* not provided, this wrapper defaults to the current window.
* @param {null|object} [params.viewport] The optional viewport
* override. Passing `null` clears the viewport override.
* @param {null|number} [params.devicePixelRatio] The optional
* device pixel ratio override. Passing `null` clears the DPR
* override.
* @param {Array.<string>} [params.userContexts] The
* optional user contexts to update.
* @returns {Promise<void>} Resolves when the viewport settings
* are successfully applied.
*/
set_viewport: function(params) {
assertBidiIsEnabled();
return window.test_driver_internal.bidi.browsing_context
.set_viewport(params);
},
},
/**
* `speculation <https://wicg.github.io/nav-speculation/prefetch.html>`_ module.
*/
Expand Down Expand Up @@ -2469,6 +2505,12 @@
}
}
},
browsing_context: {
set_viewport: function() {
throw new Error(
'bidi.browsing_context.set_viewport is not implemented by testdriver-vendor.js');
},
},
emulation: {
set_geolocation_override: function (params) {
throw new Error(
Expand Down
49 changes: 49 additions & 0 deletions tools/wptrunner/wptrunner/executors/asyncactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,54 @@ async def execute(self, context: str, payload: Mapping[str, Any]) -> Any:
return await self.protocol.bidi_bluetooth.simulate_descriptor_response(
context, address, service_uuid, characteristic_uuid, descriptor_uuid, type, code, data)

class BidiBrowsingContextSetViewportAction:
name = "bidi.browsing_context.set_viewport"

def __init__(self, logger, protocol):
do_delayed_imports()
self.logger = logger
self.protocol = protocol

async def __call__(self, payload):
if "context" in payload and "userContexts" in payload:
raise ValueError("`context` and `userContexts` are mutually exclusive")

if "context" in payload:
if payload["context"] is None:
raise ValueError("`context` must not be null")
context = get_browsing_context_id(payload["context"])
else:
context = None

viewport = (
payload["viewport"]
if "viewport" in payload
else webdriver.bidi.undefined.UNDEFINED
)
device_pixel_ratio = (
payload["devicePixelRatio"]
if "devicePixelRatio" in payload
else webdriver.bidi.undefined.UNDEFINED
)

if "userContexts" in payload:
user_contexts = payload["userContexts"]
if user_contexts is None:
raise ValueError("`userContexts` must not be null")
if len(user_contexts) == 0:
raise ValueError("At least one user context must be provided")
else:
user_contexts = None

if context is None and user_contexts is None:
raise ValueError("Either `context` or `userContexts` must be provided")

return await self.protocol.bidi_browsing_context.set_viewport(
context=context,
viewport=viewport,
device_pixel_ratio=device_pixel_ratio,
user_contexts=user_contexts)

class BidiEmulationSetGeolocationOverrideAction:
name = "bidi.emulation.set_geolocation_override"

Expand Down Expand Up @@ -354,6 +402,7 @@ async def __call__(self, payload):
BidiBluetoothSimulateCharacteristicResponseAction,
BidiBluetoothSimulateDescriptorAction,
BidiBluetoothSimulateDescriptorResponseAction,
BidiBrowsingContextSetViewportAction,
BidiEmulationSetGeolocationOverrideAction,
BidiEmulationSetLocaleOverrideAction,
BidiEmulationSetScreenOrientationOverrideAction,
Expand Down
16 changes: 15 additions & 1 deletion tools/wptrunner/wptrunner/executors/executorwebdriver.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,15 @@
WebExtensionsProtocolPart,
merge_dicts)

from typing import Any, List, Dict, Optional
from typing import Any, List, Dict, Optional, Mapping, TYPE_CHECKING, Union
from webdriver.client import Session
from webdriver import error as webdriver_error
from webdriver.bidi import error as webdriver_bidi_error
from webdriver.bidi.protocol import bidi_deserialize

if TYPE_CHECKING:
from webdriver.bidi.undefined import Undefined

here = os.path.dirname(__file__)


Expand Down Expand Up @@ -278,6 +281,17 @@ async def handle_user_prompt(self,
await self.webdriver.bidi_session.browsing_context.handle_user_prompt(
context=context, accept=accept, user_text=user_text)

async def set_viewport(self,
context: Optional[str],
viewport: Union[Optional[Mapping[str, Any]], "Undefined"],
device_pixel_ratio: Union[Optional[float], "Undefined"],
user_contexts: Optional[List[str]]) -> None:
await self.webdriver.bidi_session.browsing_context.set_viewport(
context=context,
viewport=viewport,
device_pixel_ratio=device_pixel_ratio,
user_contexts=user_contexts)


class WebDriverBidiEventsProtocolPart(BidiEventsProtocolPart):
_subscriptions: List[str] = []
Expand Down
16 changes: 16 additions & 0 deletions tools/wptrunner/wptrunner/executors/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,22 @@ async def handle_user_prompt(self,
"""
pass

@abstractmethod
async def set_viewport(self,
context: Optional[str],
viewport: Union[Optional[Mapping[str, Any]], "Undefined"],
device_pixel_ratio: Union[Optional[float], "Undefined"],
user_contexts: Optional[List[str]]) -> None:
"""
Modifies specific viewport characteristics for a browsing context or
user contexts.

`viewport=None` or `device_pixel_ratio=None` clears the corresponding
override.
Passing `UNDEFINED` means the corresponding field is omitted from the
BiDi command.
"""
pass

class BidiEventsProtocolPart(ProtocolPart):
"""Protocol part for managing BiDi events"""
Expand Down
21 changes: 21 additions & 0 deletions tools/wptrunner/wptrunner/testdriver-extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,27 @@
'bluetooth.descriptorEventGenerated', on_event);
};

window.test_driver_internal.bidi.browsing_context = {
set_viewport: function(params) {
const actionParams = {...(params ?? {})};
const hasContext = "context" in actionParams &&
actionParams.context !== undefined;
const hasUserContexts = "userContexts" in actionParams &&
actionParams.userContexts !== undefined;

if (hasContext && hasUserContexts) {
throw new Error(
"`context` and `userContexts` are mutually exclusive in set_viewport");
}
if (!hasContext && !hasUserContexts) {
// browsingContext.setViewport requires exactly one of `context`
// or `userContexts`. Default to the current window if neither was provided.
actionParams.context = window;
}
return create_action("bidi.browsing_context.set_viewport", actionParams);
}
};

window.test_driver_internal.bidi.emulation.set_geolocation_override =
function (params) {
if ('coordinates' in params && 'error' in params) {
Expand Down
Loading