Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
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
11 changes: 9 additions & 2 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ late CanvasKit canvasKit;
/// static APIs.
///
/// See, e.g. [SkPaint].
///
/// This also acts as a cache of an initialized CanvasKit instance. We can use
/// this, for example, to perform a hot restart without needing to redownload
/// and reinitialize CanvasKit.
@JS('window.flutterCanvasKit')
external set windowFlutterCanvasKit(CanvasKit? value);

@JS('window.flutterCanvasKit')
external set windowFlutterCanvasKit(CanvasKit value);
external CanvasKit? get windowFlutterCanvasKit;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use a more generic name so this method is compatible with Cobalt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I purposely left it open because it's unknown if we will need to call CanvasKitInit on the Cobalt implementation. So far now this is flutter-specific because it should be an already-initialized CanvasKit instance. If it turns out that the native CanvasKit instance is already initialized, we can rename this to be the same as the native CanvasKit object.


@JS()
@anonymous
Expand Down Expand Up @@ -141,7 +148,7 @@ class ColorSpace {}
@anonymous
class SkWebGLContextOptions {
external factory SkWebGLContextOptions({
required int anitalias,
required int antialias,
// WebGL version: 1 or 2.
required int majorVersion,
});
Expand Down
31 changes: 17 additions & 14 deletions lib/web_ui/lib/src/engine/canvaskit/initialization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,23 @@ String canvasKitWasmModuleUrl(String file) => canvasKitBuildUrl + file;
/// This calls `CanvasKitInit` and assigns the global [canvasKit] object.
Future<void> initializeCanvasKit() {
final Completer<void> canvasKitCompleter = Completer<void>();
late StreamSubscription<html.Event> loadSubscription;
loadSubscription = domRenderer.canvasKitScript!.onLoad.listen((_) {
loadSubscription.cancel();
final CanvasKitInitPromise canvasKitInitPromise =
CanvasKitInit(CanvasKitInitOptions(
locateFile: js.allowInterop(
(String file, String unusedBase) => canvasKitWasmModuleUrl(file)),
));
canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) {
canvasKit = ck;
windowFlutterCanvasKit = canvasKit;
canvasKitCompleter.complete();
}));
});
if (windowFlutterCanvasKit != null) {
canvasKit = windowFlutterCanvasKit!;
canvasKitCompleter.complete();
} else {
domRenderer.canvasKitLoaded!.then((_) {
final CanvasKitInitPromise canvasKitInitPromise =
CanvasKitInit(CanvasKitInitOptions(
locateFile: js.allowInterop(
(String file, String unusedBase) => canvasKitWasmModuleUrl(file)),
));
canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) {
canvasKit = ck;
windowFlutterCanvasKit = canvasKit;
canvasKitCompleter.complete();
}));
});
}

/// Add a Skia scene host.
skiaSceneHost = html.Element.tag('flt-scene');
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/canvaskit/surface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class Surface {
SkWebGLContextOptions(
// Default to no anti-aliasing. Paint commands can be explicitly
// anti-aliased by setting their `Paint` object's `antialias` property.
anitalias: 0,
antialias: 0,
majorVersion: webGLVersion,
),
);
Expand Down
21 changes: 18 additions & 3 deletions lib/web_ui/lib/src/engine/dom_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ class DomRenderer {
html.ScriptElement? get canvasKitScript => _canvasKitScript;
html.ScriptElement? _canvasKitScript;

Future<void>? get canvasKitLoaded => _canvasKitLoaded;
Future<void>? _canvasKitLoaded;

/// The element that contains the [sceneElement].
///
/// This element is created and inserted in the HTML DOM once. It is never
Expand Down Expand Up @@ -441,7 +444,8 @@ flt-glass-pane * {

_sceneHostElement = createElement('flt-scene-host');

final html.Element semanticsHostElement = createElement('flt-semantics-host');
final html.Element semanticsHostElement =
createElement('flt-semantics-host');
semanticsHostElement.style
..position = 'absolute'
..transformOrigin = '0 0 0';
Expand Down Expand Up @@ -509,11 +513,21 @@ flt-glass-pane * {
});
}

if (useCanvasKit) {
// Only reset CanvasKit if it's not already available.
if (useCanvasKit && windowFlutterCanvasKit == null) {
_canvasKitScript?.remove();
_canvasKitScript = html.ScriptElement();
_canvasKitScript!.src = canvasKitJavaScriptBindingsUrl;

Completer<void> canvasKitLoadCompleter = Completer<void>();
_canvasKitLoaded = canvasKitLoadCompleter.future;

late StreamSubscription<html.Event> loadSubscription;
loadSubscription = _canvasKitScript!.onLoad.listen((_) {
loadSubscription.cancel();
canvasKitLoadCompleter.complete(true);
});

// TODO(hterkelsen): Rather than this monkey-patch hack, we should
// build CanvasKit ourselves. See:
// https://github.com/flutter/flutter/issues/52588
Expand Down Expand Up @@ -591,7 +605,8 @@ flt-glass-pane * {
/// logical pixels. To compensate, we inject an inverse scale at the root
/// level.
void updateSemanticsScreenProperties() {
_semanticsHostElement!.style.transform = 'scale(${1 / html.window.devicePixelRatio})';
_semanticsHostElement!.style.transform =
'scale(${1 / html.window.devicePixelRatio})';
}

/// Called immediately after browser window metrics change.
Expand Down
40 changes: 40 additions & 0 deletions lib/web_ui/test/canvaskit/hot_restart_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:test/bootstrap/browser.dart';
import 'package:test/test.dart';
import 'package:ui/src/engine.dart';

import 'package:ui/ui.dart' as ui;

import 'common.dart';

void main() {
internalBootstrapBrowserTest(() => testMain);
}

void testMain() {
group('initialize', () {
test(
're-uses the same initialized instance if it is already set on the window',
() async {
expect(windowFlutterCanvasKit, isNull);

DomRenderer();
await ui.webOnlyInitializePlatform(
assetManager: WebOnlyMockAssetManager());
expect(windowFlutterCanvasKit, isNotNull);

var firstCanvasKitInstance = windowFlutterCanvasKit;

// Triggers a reset of the CanvasKit script element.
DomRenderer();
await ui.webOnlyInitializePlatform(
assetManager: WebOnlyMockAssetManager());
// The instance is the same.
expect(firstCanvasKitInstance, windowFlutterCanvasKit);
});
// TODO: https://github.com/flutter/flutter/issues/60040
}, skip: isIosSafari);
}
3 changes: 2 additions & 1 deletion lib/web_ui/test/canvaskit/initialization_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ void testMain() {

test('populates flt-renderer and flt-build-mode', () {
DomRenderer();
expect(html.document.body!.attributes['flt-renderer'], 'canvaskit (requested explicitly)');
expect(html.document.body!.attributes['flt-renderer'],
'canvaskit (requested explicitly)');
expect(html.document.body!.attributes['flt-build-mode'], 'debug');
});
// TODO: https://github.com/flutter/flutter/issues/60040
Expand Down