Skip to content
Open
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 4.0.0-wip

* Replace usages of `Point<int>` and `Rectangle<int>` from `dart:math` with
custom `Location`, `Rect`, and `Size` types.

## 3.2.0-wip

* Require Dart 3.4 and add a dependency on `package:web`.
Expand Down
1 change: 1 addition & 0 deletions lib/async_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export 'package:webdriver/src/common/capabilities.dart';
export 'package:webdriver/src/common/command_event.dart';
export 'package:webdriver/src/common/cookie.dart';
export 'package:webdriver/src/common/exception.dart';
export 'package:webdriver/src/common/geometry.dart';
export 'package:webdriver/src/common/log.dart';
export 'package:webdriver/src/common/mouse.dart';
export 'package:webdriver/src/common/spec.dart';
Expand Down
11 changes: 5 additions & 6 deletions lib/src/async/web_element.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
// limitations under the License.

import 'dart:async';
import 'dart:math';

import '../common/by.dart';
import '../common/geometry.dart';
import '../common/request_client.dart';
import '../common/web_element.dart' as common;
import '../common/webdriver_handler.dart';
Expand Down Expand Up @@ -75,20 +75,19 @@ class WebElement extends common.WebElement implements SearchContext {
_handler.element.parseDisplayedResponse);

/// The location within the document of this element.
Future<Point<int>> get location => _client.send(
Future<Position> get location => _client.send(
_handler.element.buildLocationRequest(id),
_handler.element.parseLocationResponse);

/// The size of this element.
Future<Rectangle<int>> get size => _client.send(
_handler.element.buildSizeRequest(id),
Future<Size> get size => _client.send(_handler.element.buildSizeRequest(id),
_handler.element.parseSizeResponse);

/// The bounds of this element.
Future<Rectangle<int>> get rect async {
Future<Rect> get rect async {
final location = await this.location;
final size = await this.size;
return Rectangle<int>(location.x, location.y, size.width, size.height);
return Rect.from(topLeft: location, size: size);
}

/// The tag name for this element.
Expand Down
20 changes: 10 additions & 10 deletions lib/src/async/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
// limitations under the License.

import 'dart:async';
import 'dart:math';

import '../common/geometry.dart';
import '../common/request_client.dart';
import '../common/webdriver_handler.dart';

Expand All @@ -31,21 +31,21 @@ class Window {
_handler.window.parseSetActiveResponse);

/// The location of the window.
Future<Point<int>> get location => _client.send(
Future<Position> get location => _client.send(
_handler.window.buildLocationRequest(),
_handler.window.parseLocationResponse);

/// The outer size of the window.
Future<Rectangle<int>> get size => _client.send(
Future<Size> get size => _client.send(
_handler.window.buildSizeRequest(), _handler.window.parseSizeResponse);

/// The inner size of the window.
Future<Rectangle<int>> get innerSize => _client.send(
Future<Size> get innerSize => _client.send(
_handler.window.buildInnerSizeRequest(),
_handler.window.parseInnerSizeResponse);

/// The location and size of the window.
Future<Rectangle<int>> get rect async {
Future<Rect> get rect async {
try {
return await _client.send(_handler.window.buildRectRequest(),
_handler.window.parseRectResponse);
Expand All @@ -54,22 +54,22 @@ class Window {
// Delegate to other methods.
final location = await this.location;
final size = await this.size;
return Rectangle<int>(location.x, location.y, size.width, size.height);
return Rect.from(topLeft: location, size: size);
}
}

/// Sets the window location.
Future<void> setLocation(Point<int> point) => _client.send(
Future<void> setLocation(Position point) => _client.send(
_handler.window.buildSetLocationRequest(point),
_handler.window.parseSetLocationResponse);

/// Sets the window size.
Future<void> setSize(Rectangle<int> size) => _client.send(
Future<void> setSize(Size size) => _client.send(
_handler.window.buildSetSizeRequest(size),
_handler.window.parseSetSizeResponse);

/// The location and size of the window.
Future<void> setRect(Rectangle<int> rect) async {
Future<void> setRect(Rect rect) async {
try {
await _client.send(_handler.window.buildSetRectRequest(rect),
_handler.window.parseSetRectResponse);
Expand All @@ -78,7 +78,7 @@ class Window {
// JsonWire cannot implement this API in one call.
// Delegate to other methods.
await setLocation(rect.topLeft);
await setSize(Rectangle(0, 0, rect.width, rect.height));
await setSize(rect.size);
}
}

Expand Down
152 changes: 152 additions & 0 deletions lib/src/common/geometry.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2025 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/// A two-dimensional location represented by [x] and [y] coordinates on
/// a 2D-coordinate system where the origin `(0, 0)` is in the top-left corner.
final class Position {
/// The x-coordinate of this position.
final int x;

/// The y-coordinate of this position.
final int y;

/// Create a location at the specified [x] and [y] coordinates.
const Position({required this.x, required this.y});

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Position && x == other.x && y == other.y;

@override
int get hashCode => Object.hash(x, y);

@override
String toString() => 'Position(x: $x, y: $y)';
}

/// A rectangle, which is a quadrilateral with four right angles, represented on
/// a 2D-coordinate system where the origin `(0, 0)` is in the top-left corner.
final class Rect {
final int _left;
final int _top;
final int _width;
final int _height;

/// Create a rectangle with its upper-left corner at `(left, top)`
/// and its bottom right corner at `(left + width, top + height)`.
///
/// [width] and [height] should both be non-negative.
/// If they aren't, they are clamped to zero.
const Rect({
required int left,
required int top,
required int width,
required int height,
}) : assert(width >= 0, height >= 0),
_top = top,
_left = left,
_width = (width < 0) ? 0 : width,
_height = (height < 0) ? 0 : height;

/// Create a rectangle with its upper-left corner at `(topLeft.x, topLeft.y)`
/// and its bottom right corner at `(topLeft.x + width, topLeft.y + height)`.
///
/// The `width` and `height` of [Size] should both be non-negative.
factory Rect.from({required Position topLeft, required Size size}) => Rect(
left: topLeft.x,
top: topLeft.y,
width: size.width,
height: size.height,
);

/// The width of this rectangle.
int get width => _width;

/// The height of this rectangle.
int get height => _height;

/// The size of this rectangle.
Size get size => Size(width: _width, height: _height);

/// The x-coordinate of the left edge of this rectangle.
int get left => _left;

/// The y-coordinate of the top edge of this rectangle.
int get top => _top;

/// The x-coordinate of the right edge of this rectangle.
int get right => _left + _width;

/// The x-coordinate of the bottom edge of this rectangle.
int get bottom => _top + _height;

/// The location of the top-left corner of this rectangle.
Position get topLeft => Position(x: left, y: top);

/// The location of the top-right corner of this rectangle.
Position get topRight => Position(x: right, y: top);

/// The location of the bottom-right corner of this rectangle.
Position get bottomRight => Position(x: right, y: bottom);

/// The location of the bottom-left corner of this rectangle.
Position get bottomLeft => Position(x: left, y: bottom);

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Rect &&
_left == other._left &&
_top == other._top &&
_width == other._width &&
_height == other._height;

@override
int get hashCode => Object.hash(_left, _top, _width, _height);

@override
String toString() =>
'Rect(left: $_left, top: $_top, width: $_width, height: $_height)';
}

/// The width and height dimensions of a 2D object.
final class Size {
/// The width dimension.
final int width;

/// The height dimension.
final int height;

/// Create a size that represents the
/// specified [width] and [height] dimensions.
///
/// [width] and [height] should both be non-negative.
/// If they aren't, they are clamped to zero.
const Size({required int width, required int height})
: assert(width >= 0, height >= 0),
width = (width < 0) ? 0 : width,
height = (height < 0) ? 0 : height;

@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Size && width == other.width && height == other.height;

@override
int get hashCode => Object.hash(width, height);

@override
String toString() => 'Size(width: $width, height: $height)';
}
20 changes: 9 additions & 11 deletions lib/src/common/webdriver_handler.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'dart:math';

import '../../async_core.dart';
import 'request.dart';
import 'session.dart';
Expand Down Expand Up @@ -157,15 +155,15 @@ abstract class ElementHandler {
WebDriverRequest buildLocationRequest(String elementId);

/// Parses response for 'Element Location'.
Point<int> parseLocationResponse(WebDriverResponse response);
Position parseLocationResponse(WebDriverResponse response);

/// Builds request for 'Element Size'.
WebDriverRequest buildSizeRequest(String elementId);

/// Parses response for 'Element Size'.
///
/// This will be the rectangle moved to (0, 0).
Rectangle<int> parseSizeResponse(WebDriverResponse response);
Size parseSizeResponse(WebDriverResponse response);

/// Builds request for 'Element Name'.
WebDriverRequest buildNameRequest(String elementId);
Expand Down Expand Up @@ -363,36 +361,36 @@ abstract class WindowHandler {
WebDriverRequest buildLocationRequest();

/// Parses response for 'Window Location'.
Point<int> parseLocationResponse(WebDriverResponse response);
Position parseLocationResponse(WebDriverResponse response);

/// Builds request for 'Window Size'.
WebDriverRequest buildSizeRequest();

/// Parses response for 'Window Size'.
///
/// This will be the rectangle moved to (0, 0).
Rectangle<int> parseSizeResponse(WebDriverResponse response);
Size parseSizeResponse(WebDriverResponse response);

/// Builds request for 'Window Rect'.
WebDriverRequest buildRectRequest();

/// Parses response for 'Window Rect'.
Rectangle<int> parseRectResponse(WebDriverResponse response);
Rect parseRectResponse(WebDriverResponse response);

/// Builds request for 'Set Window Location'.
WebDriverRequest buildSetLocationRequest(Point<int> location);
WebDriverRequest buildSetLocationRequest(Position location);

/// Parses response for 'Set Window Location'.
void parseSetLocationResponse(WebDriverResponse response);

/// Builds request for 'Set Window Size'.
WebDriverRequest buildSetSizeRequest(Rectangle<int> size);
WebDriverRequest buildSetSizeRequest(Size size);

/// Parses response for 'Set Window Size'.
void parseSetSizeResponse(WebDriverResponse response);

/// Builds request for 'Set Window Rect'.
WebDriverRequest buildSetRectRequest(Rectangle<int> rect);
WebDriverRequest buildSetRectRequest(Rect rect);

/// Parses response for 'Set Window Rect'.
void parseSetRectResponse(WebDriverResponse response);
Expand Down Expand Up @@ -423,7 +421,7 @@ abstract class WindowHandler {
WebDriverRequest buildInnerSizeRequest();

/// Parses response for 'Inner Size'.
Rectangle<int> parseInnerSizeResponse(WebDriverResponse response);
Size parseInnerSizeResponse(WebDriverResponse response);
}

abstract class FrameHandler {
Expand Down
24 changes: 12 additions & 12 deletions lib/src/handler/json_wire/element.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'dart:math';

import '../../common/geometry.dart';
import '../../common/request.dart';
import '../../common/webdriver_handler.dart';
import 'utils.dart';
Expand Down Expand Up @@ -63,23 +62,24 @@ class JsonWireElementHandler extends ElementHandler {
WebDriverRequest.getRequest('${elementPrefix(elementId)}location');

@override
Point<int> parseLocationResponse(WebDriverResponse response) {
final point = parseJsonWireResponse(response) as Map;
return Point((point['x'] as num).toInt(), (point['y'] as num).toInt());
Position parseLocationResponse(WebDriverResponse response) {
final point = parseJsonWireResponse(response) as Map<String, Object?>;
return Position(
x: (point['x'] as num).toInt(),
y: (point['y'] as num).toInt(),
);
}

@override
WebDriverRequest buildSizeRequest(String elementId) =>
WebDriverRequest.getRequest('${elementPrefix(elementId)}size');

@override
Rectangle<int> parseSizeResponse(WebDriverResponse response) {
final size = parseJsonWireResponse(response) as Map;
return Rectangle<int>(
0,
0,
(size['width'] as num).toInt(),
(size['height'] as num).toInt(),
Size parseSizeResponse(WebDriverResponse response) {
final size = parseJsonWireResponse(response) as Map<String, Object?>;
return Size(
width: (size['width'] as num).toInt(),
height: (size['height'] as num).toInt(),
);
}

Expand Down
Loading