Skip to content

Commit 170267b

Browse files
authored
[web_benchmarks] migrate to pkg:web (#5592)
Drop dart:html Enables usage via WebAssembly Fixes flutter/flutter#139577
1 parent c0b30f1 commit 170267b

File tree

6 files changed

+64
-54
lines changed

6 files changed

+64
-54
lines changed

packages/web_benchmarks/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.1.0+11
2+
3+
* Migrates benchmark recorder from `dart:html` to `package:web` to support WebAssembly.
4+
15
## 0.1.0+10
26

37
* Ensure the benchmark client reloads with the proper `initialPage`.

packages/web_benchmarks/lib/client.dart

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
import 'dart:async';
66
import 'dart:convert' show json;
7-
import 'dart:html' as html;
7+
import 'dart:js_interop';
88
import 'dart:math' as math;
99

10+
import 'package:web/helpers.dart';
11+
1012
import 'src/common.dart';
1113
import 'src/recorder.dart';
14+
1215
export 'src/recorder.dart';
1316

1417
/// Signature for a function that creates a [Recorder].
@@ -47,7 +50,7 @@ Future<void> runBenchmarks(
4750

4851
await _runBenchmark(nextBenchmark);
4952

50-
final Uri currentUri = Uri.parse(html.window.location.href);
53+
final Uri currentUri = Uri.parse(window.location.href);
5154
// Create a new URI with the current 'page' value set to [initialPage] to
5255
// ensure the benchmark app is reloaded at the proper location.
5356
final Uri newUri = Uri(
@@ -58,7 +61,7 @@ Future<void> runBenchmarks(
5861
);
5962

6063
// Reloading the window will trigger the next benchmark to run.
61-
html.window.location.replace(newUri.toString());
64+
window.location.replace(newUri.toString());
6265
}
6366

6467
Future<void> _runBenchmark(String? benchmarkName) async {
@@ -116,7 +119,7 @@ Future<void> _runBenchmark(String? benchmarkName) async {
116119
}
117120

118121
void _fallbackToManual(String error) {
119-
html.document.body!.appendHtml('''
122+
document.body!.appendHtml('''
120123
<div id="manual-panel">
121124
<h3>$error</h3>
122125
@@ -127,33 +130,33 @@ void _fallbackToManual(String error) {
127130
${_benchmarks.keys.map((String name) => '<li><button id="$name">$name</button></li>').join('\n')}
128131
</ul>
129132
</div>
130-
''',
131-
validator: html.NodeValidatorBuilder()
132-
..allowHtml5()
133-
..allowInlineStyles());
133+
''');
134134

135135
for (final String benchmarkName in _benchmarks.keys) {
136136
// Find the button elements added above.
137-
final html.Element button = html.document.querySelector('#$benchmarkName')!;
138-
button.addEventListener('click', (_) {
139-
final html.Element? manualPanel =
140-
html.document.querySelector('#manual-panel');
141-
manualPanel?.remove();
142-
_runBenchmark(benchmarkName);
143-
});
137+
final Element button = document.querySelector('#$benchmarkName')!;
138+
button.addEventListener(
139+
'click',
140+
(JSAny? arg) {
141+
final Element? manualPanel = document.querySelector('#manual-panel');
142+
manualPanel?.remove();
143+
_runBenchmark(benchmarkName);
144+
}.toJS);
144145
}
145146
}
146147

147148
/// Visualizes results on the Web page for manual inspection.
148149
void _printResultsToScreen(Profile profile) {
149-
final html.BodyElement body = html.document.body!;
150+
final HTMLBodyElement body = document.body! as HTMLBodyElement;
150151

151-
body.innerHtml = '<h2>${profile.name}</h2>';
152+
body.innerHTML = '<h2>${profile.name}</h2>';
152153

153154
profile.scoreData.forEach((String scoreKey, Timeseries timeseries) {
154155
body.appendHtml('<h2>$scoreKey</h2>');
155156
body.appendHtml('<pre>${timeseries.computeStats()}</pre>');
156-
body.append(TimeseriesVisualization(timeseries).render());
157+
// TODO(kevmoo): remove `NodeGlue` cast when we no longer need to support
158+
// pkg:web 0.3.0
159+
NodeGlue(body).append(TimeseriesVisualization(timeseries).render());
157160
});
158161
}
159162

@@ -162,10 +165,10 @@ class TimeseriesVisualization {
162165
/// Creates a visualization for a [Timeseries].
163166
TimeseriesVisualization(this._timeseries) {
164167
_stats = _timeseries.computeStats();
165-
_canvas = html.CanvasElement();
166-
_screenWidth = html.window.screen!.width!;
168+
_canvas = CanvasElement();
169+
_screenWidth = window.screen.width;
167170
_canvas.width = _screenWidth;
168-
_canvas.height = (_kCanvasHeight * html.window.devicePixelRatio).round();
171+
_canvas.height = (_kCanvasHeight * window.devicePixelRatio).round();
169172
_canvas.style
170173
..width = '100%'
171174
..height = '${_kCanvasHeight}px'
@@ -186,8 +189,8 @@ class TimeseriesVisualization {
186189

187190
final Timeseries _timeseries;
188191
late TimeseriesStats _stats;
189-
late html.CanvasElement _canvas;
190-
late html.CanvasRenderingContext2D _ctx;
192+
late CanvasElement _canvas;
193+
late CanvasRenderingContext2D _ctx;
191194
late int _screenWidth;
192195

193196
// Used to normalize benchmark values to chart height.
@@ -209,9 +212,9 @@ class TimeseriesVisualization {
209212
}
210213

211214
/// Renders the timeseries into a `<canvas>` and returns the canvas element.
212-
html.CanvasElement render() {
213-
_ctx.translate(0, _kCanvasHeight * html.window.devicePixelRatio);
214-
_ctx.scale(1, -html.window.devicePixelRatio);
215+
CanvasElement render() {
216+
_ctx.translate(0, _kCanvasHeight * window.devicePixelRatio);
217+
_ctx.scale(1, -window.devicePixelRatio);
215218

216219
final double barWidth = _screenWidth / _stats.samples.length;
217220
double xOffset = 0;
@@ -220,19 +223,19 @@ class TimeseriesVisualization {
220223

221224
if (sample.isWarmUpValue) {
222225
// Put gray background behind warm-up samples.
223-
_ctx.fillStyle = 'rgba(200,200,200,1)';
226+
_ctx.fillStyle = 'rgba(200,200,200,1)'.toJS;
224227
_ctx.fillRect(xOffset, 0, barWidth, _normalized(_maxValueChartRange));
225228
}
226229

227230
if (sample.magnitude > _maxValueChartRange) {
228231
// The sample value is so big it doesn't fit on the chart. Paint it purple.
229-
_ctx.fillStyle = 'rgba(100,50,100,0.8)';
232+
_ctx.fillStyle = 'rgba(100,50,100,0.8)'.toJS;
230233
} else if (sample.isOutlier) {
231234
// The sample is an outlier, color it light red.
232-
_ctx.fillStyle = 'rgba(255,50,50,0.6)';
235+
_ctx.fillStyle = 'rgba(255,50,50,0.6)'.toJS;
233236
} else {
234237
// A non-outlier sample, color it light blue.
235-
_ctx.fillStyle = 'rgba(50,50,255,0.6)';
238+
_ctx.fillStyle = 'rgba(50,50,255,0.6)'.toJS;
236239
}
237240

238241
_ctx.fillRect(xOffset, 0, barWidth - 1, _normalized(sample.magnitude));
@@ -245,12 +248,12 @@ class TimeseriesVisualization {
245248
_normalized(_stats.average));
246249

247250
// Draw a horizontal dashed line corresponding to the outlier cut off.
248-
_ctx.setLineDash(<num>[5, 5]);
251+
_ctx.setLineDash(<JSNumber>[5.toJS, 5.toJS].toJS);
249252
drawLine(0, _normalized(_stats.outlierCutOff), _screenWidth,
250253
_normalized(_stats.outlierCutOff));
251254

252255
// Draw a light red band that shows the noise (1 stddev in each direction).
253-
_ctx.fillStyle = 'rgba(255,50,50,0.3)';
256+
_ctx.fillStyle = 'rgba(255,50,50,0.3)'.toJS;
254257
_ctx.fillRect(
255258
0,
256259
_normalized(_stats.average * (1 - _stats.noise)),
@@ -283,7 +286,7 @@ class LocalBenchmarkServerClient {
283286
/// Returns [kManualFallback] if local server is not available (uses 404 as a
284287
/// signal).
285288
Future<String> requestNextBenchmark() async {
286-
final html.HttpRequest request = await _requestXhr(
289+
final XMLHttpRequest request = await _requestXhr(
287290
'/next-benchmark',
288291
method: 'POST',
289292
mimeType: 'application/json',
@@ -298,7 +301,7 @@ class LocalBenchmarkServerClient {
298301
}
299302

300303
isInManualMode = false;
301-
return request.responseText ?? kManualFallback;
304+
return request.responseText;
302305
}
303306

304307
void _checkNotManualMode() {
@@ -314,7 +317,7 @@ class LocalBenchmarkServerClient {
314317
/// DevTools Protocol.
315318
Future<void> startPerformanceTracing(String? benchmarkName) async {
316319
_checkNotManualMode();
317-
await html.HttpRequest.request(
320+
await HttpRequest.request(
318321
'/start-performance-tracing?label=$benchmarkName',
319322
method: 'POST',
320323
mimeType: 'application/json',
@@ -324,7 +327,7 @@ class LocalBenchmarkServerClient {
324327
/// Stops the performance tracing session started by [startPerformanceTracing].
325328
Future<void> stopPerformanceTracing() async {
326329
_checkNotManualMode();
327-
await html.HttpRequest.request(
330+
await HttpRequest.request(
328331
'/stop-performance-tracing',
329332
method: 'POST',
330333
mimeType: 'application/json',
@@ -335,7 +338,7 @@ class LocalBenchmarkServerClient {
335338
/// server.
336339
Future<void> sendProfileData(Profile profile) async {
337340
_checkNotManualMode();
338-
final html.HttpRequest request = await html.HttpRequest.request(
341+
final XMLHttpRequest request = await _requestXhr(
339342
'/profile-data',
340343
method: 'POST',
341344
mimeType: 'application/json',
@@ -352,7 +355,7 @@ class LocalBenchmarkServerClient {
352355
/// The server will halt the devicelab task and log the error.
353356
Future<void> reportError(dynamic error, StackTrace stackTrace) async {
354357
_checkNotManualMode();
355-
await html.HttpRequest.request(
358+
await HttpRequest.request(
356359
'/on-error',
357360
method: 'POST',
358361
mimeType: 'application/json',
@@ -366,31 +369,35 @@ class LocalBenchmarkServerClient {
366369
/// Reports a message about the demo to the benchmark server.
367370
Future<void> printToConsole(String report) async {
368371
_checkNotManualMode();
369-
await html.HttpRequest.request(
372+
await HttpRequest.request(
370373
'/print-to-console',
371374
method: 'POST',
372375
mimeType: 'text/plain',
373376
sendData: report,
374377
);
375378
}
376379

377-
/// This is the same as calling [html.HttpRequest.request] but it doesn't
380+
/// This is the same as calling [XMLHttpRequest.request] but it doesn't
378381
/// crash on 404, which we use to detect `flutter run`.
379-
Future<html.HttpRequest> _requestXhr(
382+
Future<XMLHttpRequest> _requestXhr(
380383
String url, {
381384
required String method,
382385
required String mimeType,
383-
required dynamic sendData,
386+
required String sendData,
384387
}) {
385-
final Completer<html.HttpRequest> completer = Completer<html.HttpRequest>();
386-
final html.HttpRequest xhr = html.HttpRequest();
387-
xhr.open(method, url, async: true);
388+
final Completer<XMLHttpRequest> completer = Completer<XMLHttpRequest>();
389+
final XMLHttpRequest xhr = XMLHttpRequest();
390+
xhr.open(method, url, true);
388391
xhr.overrideMimeType(mimeType);
389-
xhr.onLoad.listen((html.ProgressEvent e) {
392+
xhr.onLoad.listen((ProgressEvent e) {
390393
completer.complete(xhr);
391394
});
392395
xhr.onError.listen(completer.completeError);
393-
xhr.send(sendData);
396+
xhr.send(sendData.toJS);
394397
return completer.future;
395398
}
396399
}
400+
401+
extension on HTMLElement {
402+
void appendHtml(String input) => insertAdjacentHTML('beforeend', input);
403+
}

packages/web_benchmarks/lib/src/recorder.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6-
import 'dart:html' as html;
6+
import 'dart:js_interop';
77
import 'dart:math' as math;
88
import 'dart:ui';
99
import 'dart:ui_web' as ui_web;
@@ -15,6 +15,7 @@ import 'package:flutter/scheduler.dart';
1515
import 'package:flutter/services.dart';
1616
import 'package:flutter/widgets.dart';
1717
import 'package:meta/meta.dart';
18+
import 'package:web/helpers.dart' as html;
1819

1920
import 'common.dart';
2021

@@ -1188,7 +1189,7 @@ void endMeasureFrame() {
11881189
html.window.performance.mark('measured_frame_end#$_currentFrameNumber');
11891190
html.window.performance.measure(
11901191
'measured_frame',
1191-
'measured_frame_start#$_currentFrameNumber',
1192+
'measured_frame_start#$_currentFrameNumber'.toJS,
11921193
'measured_frame_end#$_currentFrameNumber',
11931194
);
11941195

packages/web_benchmarks/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: web_benchmarks
22
description: A benchmark harness for performance-testing Flutter apps in Chrome.
33
repository: https://github.com/flutter/packages/tree/main/packages/web_benchmarks
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+web_benchmarks%22
5-
version: 0.1.0+10
5+
version: 0.1.0+11
66

77
environment:
88
sdk: ">=3.2.0 <4.0.0"
@@ -20,6 +20,7 @@ dependencies:
2020
shelf: ^1.2.0
2121
shelf_static: ^1.1.0
2222
test: ^1.19.5
23+
web: '>=0.3.0 <0.5.0'
2324
webkit_inspection_protocol: ^1.0.0
2425

2526
topics:

packages/web_benchmarks/testing/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ do the following:
1111
* Fetch dependencies for the `test_app` directory inside `testing`:
1212

1313
```bash
14-
flutter pub get testing/test_app
14+
flutter pub get --directory testing/test_app
1515
```
1616

1717
* Fetch dependencies for the `web_benchmarks` directory:

packages/web_benchmarks/testing/test_app/.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@
3131
.pub/
3232
/build/
3333

34-
# Web related
35-
lib/generated_plugin_registrant.dart
36-
3734
# Symbolication related
3835
app.*.symbols
3936

0 commit comments

Comments
 (0)