Skip to content

Commit 1eafb40

Browse files
authored
Use H5vcc CanvasKit implementation if it is detected. (flutter#31191)
1 parent ba8f1c5 commit 1eafb40

File tree

4 files changed

+114
-0
lines changed

4 files changed

+114
-0
lines changed

lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ import '../profiler.dart';
2525
/// Entrypoint into the CanvasKit API.
2626
late CanvasKit canvasKit;
2727

28+
/// Whether to use a CanvasKit implementation provided by a JavaScript
29+
/// `window.h5vcc.canvasKit` object.
30+
///
31+
/// Cobalt may use this object to expose a native implementation of the
32+
/// CanvasKit bindings. If this exists, use it instead of using the normal
33+
/// downloaded CanvasKit library.
34+
final bool useH5vccCanvasKit = h5vcc != null;
35+
2836
/// Sets the [CanvasKit] object on `window` so we can use `@JS()` to bind to
2937
/// static APIs.
3038
///
@@ -39,6 +47,18 @@ external set windowFlutterCanvasKit(CanvasKit? value);
3947
@JS('window.flutterCanvasKit')
4048
external CanvasKit? get windowFlutterCanvasKit;
4149

50+
@JS('window.h5vcc')
51+
external H5vcc? get h5vcc;
52+
53+
@JS('window.h5vcc')
54+
external set debugH5vccSetter(H5vcc? value);
55+
56+
@JS()
57+
@anonymous
58+
abstract class H5vcc {
59+
external CanvasKit? get canvasKit;
60+
}
61+
4262
@JS()
4363
@anonymous
4464
class CanvasKit {
@@ -132,6 +152,15 @@ class CanvasKit {
132152
Object src,
133153
SkPartialImageInfo info,
134154
);
155+
156+
/// Gets a Skia surface from Cobalt's h5vcc object.
157+
///
158+
/// This is only applicable when running on Cobalt and when using Cobalt's
159+
/// h5vcc CanvasKit bindings.
160+
///
161+
/// On Cobalt, this is the only way to get a Skia surface. Other CanvasKit
162+
/// Make...Surface methods are not supported.
163+
external SkSurface getH5vccSkSurface();
135164
}
136165

137166
@JS('window.CanvasKitInit')

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import '../embedder.dart';
1313
import '../safe_browser_api.dart';
1414
import 'canvaskit_api.dart';
1515
import 'fonts.dart';
16+
import 'util.dart';
1617

1718
/// Whether to use CanvasKit as the rendering backend.
1819
final bool useCanvasKit = FlutterConfiguration.flutterWebAutoDetect
@@ -47,6 +48,12 @@ String canvasKitWasmModuleUrl(String canvasKitBase, String file) =>
4748
Future<void> initializeCanvasKit({String? canvasKitBase}) async {
4849
if (windowFlutterCanvasKit != null) {
4950
canvasKit = windowFlutterCanvasKit!;
51+
} else if (useH5vccCanvasKit) {
52+
if (h5vcc?.canvasKit == null) {
53+
throw CanvasKitError('H5vcc CanvasKit implementation not found.');
54+
}
55+
canvasKit = h5vcc!.canvasKit!;
56+
windowFlutterCanvasKit = canvasKit;
5057
} else {
5158
canvasKit = await downloadCanvasKit(canvasKitBase: canvasKitBase);
5259
windowFlutterCanvasKit = canvasKit;

lib/web_ui/lib/src/engine/canvaskit/surface.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ class Surface {
137137

138138
/// Creates a <canvas> and SkSurface for the given [size].
139139
CkSurface createOrUpdateSurface(ui.Size size) {
140+
if (useH5vccCanvasKit) {
141+
_surface ??= CkSurface(canvasKit.getH5vccSkSurface(), null);
142+
return _surface!;
143+
}
144+
140145
if (size.isEmpty) {
141146
throw CanvasKitError('Cannot create surfaces of empty size.');
142147
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
import 'dart:html' as html;
6+
import 'dart:js' as js;
7+
8+
import 'package:test/bootstrap/browser.dart';
9+
import 'package:test/test.dart';
10+
import 'package:ui/src/engine.dart';
11+
import 'package:ui/ui.dart' as ui;
12+
13+
import 'common.dart';
14+
15+
void main() {
16+
internalBootstrapBrowserTest(() => testMain);
17+
}
18+
19+
void testMain() {
20+
group('H5vcc patched CanvasKit', () {
21+
int getH5vccSkSurfaceCalledCount = 0;
22+
23+
setUpAll(() async {
24+
// Set `window.h5vcc` to PatchedH5vcc which uses a downloaded CanvasKit.
25+
final CanvasKit downloadedCanvasKit = await downloadCanvasKit();
26+
debugH5vccSetter = PatchedH5vcc(downloadedCanvasKit);
27+
28+
// Monkey-patch the getH5vccSkSurface function of
29+
// `window.h5vcc.canvasKit`.
30+
js.context['h5vcc']['canvasKit']['getH5vccSkSurface'] = () {
31+
getH5vccSkSurfaceCalledCount++;
32+
33+
// Returns a fake [SkSurface] object with a minimal implementation.
34+
return js.JsObject.jsify(<String, dynamic>{
35+
'dispose': () {}
36+
});
37+
};
38+
});
39+
40+
setUpCanvasKitTest();
41+
42+
setUp(() {
43+
getH5vccSkSurfaceCalledCount = 0;
44+
});
45+
46+
test('sets useH5vccCanvasKit', () {
47+
expect(useH5vccCanvasKit, true);
48+
});
49+
50+
test('API includes patched getH5vccSkSurface', () {
51+
expect(canvasKit.getH5vccSkSurface, isNotNull);
52+
});
53+
54+
test('Surface acquireFrame uses getH5vccSkSurface', () {
55+
final Surface surface = SurfaceFactory.instance.getSurface();
56+
surface.acquireFrame(ui.Size.zero);
57+
expect(getH5vccSkSurfaceCalledCount, 1);
58+
59+
// No <canvas> element should be created.
60+
expect(
61+
flutterViewEmbedder.glassPaneElement!.querySelectorAll<html.Element>('canvas'),
62+
isEmpty,
63+
);
64+
});
65+
}, testOn: 'chrome');
66+
}
67+
68+
class PatchedH5vcc implements H5vcc {
69+
@override
70+
final CanvasKit canvasKit;
71+
72+
PatchedH5vcc(this.canvasKit);
73+
}

0 commit comments

Comments
 (0)