Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 659fe3a

Browse files
committed
[web] Introduce platform message to enable experiment flags on web
1 parent c1111bb commit 659fe3a

File tree

9 files changed

+163
-27
lines changed

9 files changed

+163
-27
lines changed

lib/web_ui/lib/src/engine.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ part 'engine/text_editing/text_editing.dart';
117117
part 'engine/util.dart';
118118
part 'engine/validators.dart';
119119
part 'engine/vector_math.dart';
120+
part 'engine/web_experiments.dart';
120121
part 'engine/window.dart';
121122

122123
bool _engineInitialized = false;

lib/web_ui/lib/src/engine/compositor/initialization.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
part of engine;
77

88
/// EXPERIMENTAL: Enable the Skia-based rendering backend.
9-
const bool experimentalUseSkia =
10-
bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false);
9+
bool get experimentalUseSkia => webExperiments.useSkia;
1110

1211
/// The URL to use when downloading the CanvasKit script and associated wasm.
1312
///

lib/web_ui/lib/src/engine/text/measurement.dart

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,6 @@ abstract class TextMeasurementService {
187187
static TextMeasurementService get canvasInstance =>
188188
CanvasTextMeasurementService.instance;
189189

190-
/// Whether the new experimental implementation of canvas-based text
191-
/// measurement is enabled or not.
192-
///
193-
/// This is only used for testing at the moment. Once the implementation is
194-
/// complete and production-ready, we'll get rid of this flag.
195-
static bool enableExperimentalCanvasImplementation = const bool.fromEnvironment('FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT', defaultValue: false);
196-
197190
/// Gets the appropriate [TextMeasurementService] instance for the given
198191
/// [paragraph].
199192
static TextMeasurementService forParagraph(ui.Paragraph paragraph) {
@@ -206,7 +199,7 @@ abstract class TextMeasurementService {
206199
// Skip using canvas measurements until the iframe becomes visible.
207200
// see: https://github.com/flutter/flutter/issues/36341
208201
if (!window.physicalSize.isEmpty &&
209-
enableExperimentalCanvasImplementation &&
202+
webExperiments.useCanvasText &&
210203
_canUseCanvasMeasurement(paragraph)) {
211204
return canvasInstance;
212205
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// @dart = 2.6
6+
part of engine;
7+
8+
const MethodCodec codec = JSONMethodCodec();
9+
10+
final WebExperiments webExperiments = WebExperiments._();
11+
12+
/// A bag of all experiment flags in the web engine.
13+
///
14+
/// This class also handles platform messages that can be sent to enable/disable
15+
/// certain experiments at runtime without the need to access engine internals.
16+
class WebExperiments {
17+
WebExperiments._();
18+
19+
/// Experiment flag for using the Skia-based rendering backend.
20+
bool get useSkia => _useSkia ?? false;
21+
set useSkia(bool enabled) {
22+
_useSkia = enabled;
23+
}
24+
bool _useSkia = const bool.fromEnvironment('FLUTTER_WEB_USE_SKIA');
25+
26+
/// Experiment flag for using canvas-based text measurement.
27+
bool get useCanvasText => _useCanvasText ?? false;
28+
set useCanvasText(bool enabled) {
29+
_useCanvasText = enabled;
30+
}
31+
bool _useCanvasText =
32+
const bool.fromEnvironment('FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT');
33+
34+
/// Reset all experimental flags to their default values.
35+
void reset() {
36+
_useSkia = null;
37+
_useCanvasText = null;
38+
}
39+
40+
/// Handles the platform message used to enable/disable web experiments in the
41+
/// web engine.
42+
void enableWebExperiments(Map<String, dynamic> message) {
43+
for (final String name in message.keys) {
44+
final bool enabled = message[name];
45+
switch (name) {
46+
case 'useSkia':
47+
_useSkia = enabled;
48+
break;
49+
case 'useCanvasText':
50+
_useCanvasText = enabled;
51+
break;
52+
}
53+
}
54+
}
55+
}

lib/web_ui/lib/src/engine/window.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,16 @@ class EngineWindow extends ui.Window {
210210
break;
211211
}
212212
return;
213+
214+
case 'flutter/experiments':
215+
const MethodCodec codec = JSONMethodCodec();
216+
final MethodCall decoded = codec.decodeMethodCall(data);
217+
final Map<String, dynamic> arguments = decoded.arguments;
218+
switch (decoded.method) {
219+
case 'enableWebExperiments':
220+
webExperiments.enableWebExperiments(arguments);
221+
return;
222+
}
213223
}
214224

215225
if (pluginMessageCallHandler != null) {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// @dart = 2.6
6+
import 'package:test/test.dart';
7+
import 'package:ui/src/engine.dart';
8+
9+
void main() {
10+
tearDown(() {
11+
webExperiments.reset();
12+
});
13+
14+
test('default web experiment values', () {
15+
expect(webExperiments.useSkia, false);
16+
expect(webExperiments.useCanvasText, false);
17+
});
18+
19+
test('can turn on/off web experiments', () {
20+
webExperiments.enableWebExperiments(<String, bool>{
21+
'useSkia': true,
22+
});
23+
expect(webExperiments.useSkia, true);
24+
expect(webExperiments.useCanvasText, false);
25+
26+
webExperiments.enableWebExperiments(<String, bool>{
27+
'useSkia': false,
28+
'useCanvasText': true,
29+
});
30+
expect(webExperiments.useSkia, false);
31+
expect(webExperiments.useCanvasText, true);
32+
33+
webExperiments.enableWebExperiments(<String, bool>{
34+
'useSkia': true,
35+
'useCanvasText': null,
36+
});
37+
expect(webExperiments.useSkia, true);
38+
// Goes back to default value.
39+
expect(webExperiments.useCanvasText, false);
40+
41+
webExperiments.enableWebExperiments(<String, bool>{
42+
'useSkia': null,
43+
});
44+
// Goes back to default value.
45+
expect(webExperiments.useSkia, false);
46+
expect(webExperiments.useCanvasText, false);
47+
});
48+
49+
test('can reset web experiments', () {
50+
webExperiments.enableWebExperiments(<String, bool>{
51+
'useSkia': true,
52+
'useCanvasText': false,
53+
});
54+
webExperiments.reset();
55+
expect(webExperiments.useSkia, false);
56+
expect(webExperiments.useCanvasText, false);
57+
58+
webExperiments.enableWebExperiments(<String, bool>{
59+
'useSkia': false,
60+
'useCanvasText': true,
61+
});
62+
webExperiments.reset();
63+
expect(webExperiments.useSkia, false);
64+
expect(webExperiments.useCanvasText, false);
65+
66+
webExperiments.enableWebExperiments(<String, bool>{
67+
'useSkia': true,
68+
'useCanvasText': true,
69+
});
70+
webExperiments.reset();
71+
expect(webExperiments.useSkia, false);
72+
expect(webExperiments.useCanvasText, false);
73+
});
74+
}

lib/web_ui/test/golden_tests/engine/scuba.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class EngineScubaTester {
7171
sceneElement.append(canvas.rootElement);
7272
html.document.body.append(sceneElement);
7373
String screenshotName = '${fileName}_${canvas.runtimeType}';
74-
if (TextMeasurementService.enableExperimentalCanvasImplementation) {
74+
if (webExperiments.useCanvasText) {
7575
screenshotName += '+canvas_measurement';
7676
}
7777
await diffScreenshot(
@@ -96,18 +96,20 @@ void testEachCanvas(String description, CanvasTest body,
9696
test('$description (bitmap)', () {
9797
try {
9898
TextMeasurementService.initialize(rulerCacheCapacity: 2);
99+
webExperiments.useCanvasText = false;
99100
return body(BitmapCanvas(bounds));
100101
} finally {
102+
webExperiments.useCanvasText = null;
101103
TextMeasurementService.clearCache();
102104
}
103105
});
104106
test('$description (bitmap + canvas measurement)', () async {
105107
try {
106108
TextMeasurementService.initialize(rulerCacheCapacity: 2);
107-
TextMeasurementService.enableExperimentalCanvasImplementation = true;
109+
webExperiments.useCanvasText = true;
108110
await body(BitmapCanvas(bounds));
109111
} finally {
110-
TextMeasurementService.enableExperimentalCanvasImplementation = false;
112+
webExperiments.useCanvasText = null;
111113
TextMeasurementService.clearCache();
112114
}
113115
});

lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// @dart = 2.6
66
import 'dart:async';
77

8-
import 'package:ui/ui.dart';
8+
import 'package:ui/ui.dart' hide window;
99
import 'package:ui/src/engine.dart';
1010

1111
import 'scuba.dart';
@@ -89,7 +89,7 @@ void main() async {
8989
offset = offset.translate(0, p.height + 10);
9090

9191
// Only the first line is rendered with an ellipsis.
92-
if (!TextMeasurementService.enableExperimentalCanvasImplementation) {
92+
if (!webExperiments.useCanvasText) {
9393
// This is now correct with the canvas-based measurement, so we shouldn't
9494
// print the "(wrong)" warning.
9595
p = warning('(wrong)');
@@ -106,7 +106,7 @@ void main() async {
106106

107107
// Only the first two lines are rendered and the ellipsis appears on the 2nd
108108
// line.
109-
if (!TextMeasurementService.enableExperimentalCanvasImplementation) {
109+
if (!webExperiments.useCanvasText) {
110110
// This is now correct with the canvas-based measurement, so we shouldn't
111111
// print the "(wrong)" warning.
112112
p = warning('(wrong)');

lib/web_ui/test/paragraph_test.dart

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,28 @@
44

55
// @dart = 2.6
66
import 'package:ui/src/engine.dart';
7-
import 'package:ui/ui.dart';
7+
import 'package:ui/ui.dart' hide window;
88

99
import 'package:test/test.dart';
1010

1111
void testEachMeasurement(String description, VoidCallback body, {bool skip}) {
1212
test('$description (dom measurement)', () async {
1313
try {
1414
TextMeasurementService.initialize(rulerCacheCapacity: 2);
15+
webExperiments.useCanvasText = false;
1516
return body();
1617
} finally {
18+
webExperiments.useCanvasText = null;
1719
TextMeasurementService.clearCache();
1820
}
1921
}, skip: skip);
2022
test('$description (canvas measurement)', () async {
2123
try {
2224
TextMeasurementService.initialize(rulerCacheCapacity: 2);
23-
TextMeasurementService.enableExperimentalCanvasImplementation = true;
25+
webExperiments.useCanvasText = true;
2426
return body();
2527
} finally {
26-
TextMeasurementService.enableExperimentalCanvasImplementation = false;
28+
webExperiments.useCanvasText = null;
2729
TextMeasurementService.clearCache();
2830
}
2931
}, skip: skip);
@@ -184,7 +186,7 @@ void main() async {
184186
test('getPositionForOffset multi-line', () {
185187
// [Paragraph.getPositionForOffset] for multi-line text doesn't work well
186188
// with dom-based measurement.
187-
TextMeasurementService.enableExperimentalCanvasImplementation = true;
189+
webExperiments.useCanvasText = true;
188190
TextMeasurementService.initialize(rulerCacheCapacity: 2);
189191

190192
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
@@ -280,11 +282,11 @@ void main() async {
280282
);
281283

282284
TextMeasurementService.clearCache();
283-
TextMeasurementService.enableExperimentalCanvasImplementation = false;
285+
webExperiments.useCanvasText = null;
284286
});
285287

286288
test('getPositionForOffset multi-line centered', () {
287-
TextMeasurementService.enableExperimentalCanvasImplementation = true;
289+
webExperiments.useCanvasText = true;
288290
TextMeasurementService.initialize(rulerCacheCapacity: 2);
289291

290292
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
@@ -387,7 +389,7 @@ void main() async {
387389
);
388390

389391
TextMeasurementService.clearCache();
390-
TextMeasurementService.enableExperimentalCanvasImplementation = false;
392+
webExperiments.useCanvasText = null;
391393
});
392394

393395
testEachMeasurement('getBoxesForRange returns a box', () {
@@ -782,7 +784,7 @@ void main() async {
782784

783785
test('longestLine', () {
784786
// [Paragraph.longestLine] is only supported by canvas-based measurement.
785-
TextMeasurementService.enableExperimentalCanvasImplementation = true;
787+
webExperiments.useCanvasText = true;
786788
TextMeasurementService.initialize(rulerCacheCapacity: 2);
787789

788790
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
@@ -797,7 +799,7 @@ void main() async {
797799
expect(paragraph.longestLine, 50.0);
798800

799801
TextMeasurementService.clearCache();
800-
TextMeasurementService.enableExperimentalCanvasImplementation = false;
802+
webExperiments.useCanvasText = null;
801803
});
802804

803805
testEachMeasurement('getLineBoundary (single-line)', () {
@@ -824,7 +826,7 @@ void main() async {
824826
test('getLineBoundary (multi-line)', () {
825827
// [Paragraph.getLineBoundary] for multi-line paragraphs is only supported
826828
// by canvas-based measurement.
827-
TextMeasurementService.enableExperimentalCanvasImplementation = true;
829+
webExperiments.useCanvasText = true;
828830
TextMeasurementService.initialize(rulerCacheCapacity: 2);
829831

830832
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(
@@ -867,7 +869,7 @@ void main() async {
867869
}
868870

869871
TextMeasurementService.clearCache();
870-
TextMeasurementService.enableExperimentalCanvasImplementation = false;
872+
webExperiments.useCanvasText = null;
871873
});
872874

873875
testEachMeasurement('width should be a whole integer', () {

0 commit comments

Comments
 (0)