| # Copyright 2023 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import annotations |
| |
| import enum |
| from argparse import ArgumentTypeError |
| from typing import Any, Tuple |
| |
| from crossbench import compat |
| |
| |
| @enum.unique |
| class ViewportMode(compat.StrEnum): |
| SIZE = "size" |
| MAXIMIZED = "maximized" |
| FULLSCREEN = "fullscreen" |
| HEADLESS = "headless" |
| |
| |
| class Viewport: |
| DEFAULT: Viewport |
| MAXIMIZED: Viewport |
| FULLSCREEN: Viewport |
| HEADLESS: Viewport |
| |
| @classmethod |
| def parse_sized(cls, value: Any) -> Viewport: |
| if isinstance(value, Viewport): |
| viewport = value |
| elif isinstance(value, str): |
| viewport = cls.parse(value) |
| else: |
| raise ArgumentTypeError(f"Expected str, but got '{type(value)}': {value}") |
| if not viewport.has_size: |
| raise ArgumentTypeError("Expected viewport with explicit size, " |
| f"but got {viewport}") |
| return viewport |
| |
| @classmethod |
| def parse(cls, value: str) -> Viewport: |
| if not value: |
| return cls.DEFAULT |
| if value in ("m", "max", "maximised", ViewportMode.MAXIMIZED): |
| return cls.MAXIMIZED |
| if value in ("f", "full", ViewportMode.FULLSCREEN): |
| return cls.FULLSCREEN |
| if value == ViewportMode.HEADLESS: |
| return cls.HEADLESS |
| size, _, position = value.partition(",") |
| width, _, height = size.partition("x") |
| if not height: |
| raise ArgumentTypeError(f"Missing viewport height in input: {value}") |
| x = str(cls.DEFAULT.x) |
| y = str(cls.DEFAULT.y) |
| if position: |
| x, _, y = position.partition("x") |
| if not y: |
| raise ArgumentTypeError( |
| f"Missing viewport y position in input: {value}") |
| return Viewport(int(width), int(height), int(x), int(y)) |
| |
| def __init__(self, |
| width: int = 1500, |
| height: int = 1000, |
| x: int = 10, |
| y: int = 50, |
| mode: ViewportMode = ViewportMode.SIZE): |
| self._width = width |
| self._height = height |
| self._x = x |
| self._y = y |
| self._mode = mode |
| self._validate() |
| |
| def _validate(self) -> None: |
| if self._mode == ViewportMode.SIZE: |
| if self._width <= 0: |
| raise ArgumentTypeError(f"width must be > 0, but got {self._width}") |
| if self._height <= 0: |
| raise ArgumentTypeError(f"height must be > 0, but got {self._height}") |
| if self._x < 0: |
| raise ArgumentTypeError(f"x must be >= 0, but got {self._x}") |
| if self._y < 0: |
| raise ArgumentTypeError(f"y must be >= 0, but got {self._y}") |
| else: |
| if self._width != 0: |
| raise ArgumentTypeError( |
| "Non-zero width only allowed with ViewportMode.SIZE") |
| if self._height != 0: |
| raise ArgumentTypeError( |
| "Non-zero height only allowed with ViewportMode.SIZE") |
| if self._x != 0: |
| raise ArgumentTypeError( |
| "Non-zero x only allowed with ViewportMode.SIZE") |
| if self._y != 0: |
| raise ArgumentTypeError( |
| "Non-zero y only allowed with ViewportMode.SIZE") |
| |
| @property |
| def is_default(self) -> bool: |
| return self is Viewport.DEFAULT |
| |
| @property |
| def is_maximized(self) -> bool: |
| return self._mode == ViewportMode.MAXIMIZED |
| |
| @property |
| def is_fullscreen(self) -> bool: |
| return self._mode == ViewportMode.FULLSCREEN |
| |
| @property |
| def is_headless(self) -> bool: |
| return self._mode == ViewportMode.HEADLESS |
| |
| @property |
| def has_size(self) -> bool: |
| return self._mode == ViewportMode.SIZE |
| |
| @property |
| def position(self) -> Tuple[int, int]: |
| assert self.has_size, f"Viewport has no explicit size: {self._mode}" |
| return (self._x, self._y) |
| |
| @property |
| def size(self) -> Tuple[int, int]: |
| assert self.has_size, f"Viewport has no explicit size: {self._mode}" |
| return (self._width, self._height) |
| |
| @property |
| def width(self) -> int: |
| assert self.has_size, f"Viewport has no explicit size: {self._mode}" |
| return self._width |
| |
| @property |
| def height(self) -> int: |
| assert self.has_size, f"Viewport has no explicit size: {self._mode}" |
| return self._height |
| |
| @property |
| def x(self) -> int: |
| assert self.has_size, f"Viewport has no explicit size: {self._mode}" |
| return self._x |
| |
| @property |
| def y(self) -> int: |
| assert self.has_size, f"Viewport has no explicit size: {self._mode}" |
| return self._y |
| |
| @property |
| def mode(self) -> ViewportMode: |
| return self._mode |
| |
| @property |
| def key(self) -> Tuple[Tuple, ...]: |
| return ( |
| ("mode", str(self.mode)), |
| ("x", self._x), |
| ("y", self._y), |
| ("width", self._width), |
| ("height", self._height), |
| ) |
| |
| def __str__(self) -> str: |
| if self.has_size: |
| return f"Viewport({self.width}x{self.height},{self.x}x{self.y})" |
| return f"Viewport({self.mode})" |
| |
| def __eq__(self, other) -> bool: |
| if not isinstance(other, Viewport): |
| return False |
| if self is other: |
| return True |
| return self.key == other.key |
| |
| |
| Viewport.DEFAULT = Viewport() |
| Viewport.MAXIMIZED = Viewport(0, 0, 0, 0, ViewportMode.MAXIMIZED) |
| Viewport.FULLSCREEN = Viewport(0, 0, 0, 0, ViewportMode.FULLSCREEN) |
| Viewport.HEADLESS = Viewport(0, 0, 0, 0, ViewportMode.HEADLESS) |