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

[web] Better way to detect CanvasKit variant #40154

Merged
merged 5 commits into from
Mar 15, 2023
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
4 changes: 1 addition & 3 deletions lib/web_ui/lib/src/engine/browser_detection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,4 @@ int _detectWebGLVersion() {
}

/// Whether the current browser supports the Chromium variant of CanvasKit.
const bool browserSupportsCanvaskitChromium = false;
// TODO(mdebbar): Uncomment this to enable real detection of browser support.
// final bool browserSupportsCanvaskitChromium = domIntl.v8BreakIterator != null;
final bool browserSupportsCanvaskitChromium = domIntl.v8BreakIterator != null;
Copy link
Contributor

Choose a reason for hiding this comment

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

If canvaskit_chromium also removes the wasm image codecs, then this should also add && browserSupportsImageDecoder. Although in practice, I'm not aware of any Chromium-based browser not supporting image decoders.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

90 changes: 35 additions & 55 deletions lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,9 @@ import 'renderer.dart';
/// Entrypoint into the CanvasKit API.
late CanvasKit canvasKit;

late CanvasKitVariant _canvasKitVariant;

/// Which variant of CanvasKit we are using.
CanvasKitVariant get canvasKitVariant => _canvasKitVariant;
set canvasKitVariant(CanvasKitVariant value) {
if (value == CanvasKitVariant.auto) {
throw ArgumentError.value(
value,
'value',
'CanvasKitVariant.auto is not a valid value for canvasKitVariant',
);
}
_canvasKitVariant = value;
}

// TODO(mdebbar): Turn this on when CanvasKit Chromium is ready.
// https://github.com/flutter/flutter/issues/122329
const bool _enableCanvasKitChromiumInAutoMode = false;

/// Sets the [CanvasKit] object on `window` so we can use `@JS()` to bind to
/// static APIs.
Expand Down Expand Up @@ -1884,6 +1872,13 @@ extension SkParagraphBuilderNamespaceExtension on SkParagraphBuilderNamespace {
SkParagraphStyle paragraphStyle,
TypefaceFontProvider? fontManager,
);

bool RequiresClientICU() {
if (!js_util.hasProperty(this, 'RequiresClientICU')) {
return false;
}
return js_util.callMethod(this, 'RequiresClientICU', const <Object>[],) as bool;
}
}

@JS()
Expand Down Expand Up @@ -2699,47 +2694,26 @@ void patchCanvasKitModule(DomHTMLScriptElement canvasKitScript) {
}
}

String get _canvasKitBaseUrl => configuration.canvasKitBaseUrl;

const String _kFullCanvasKitJsFileName = 'canvaskit.js';
const String _kChromiumCanvasKitJsFileName = 'chromium/canvaskit.js';

// TODO(mdebbar): Replace this with a Record once it's supported in Dart.
class _CanvasKitVariantUrl {
const _CanvasKitVariantUrl(this.url, this.variant)
: assert(
variant != CanvasKitVariant.auto,
'CanvasKitVariant.auto cannot have a url',
);

final String url;
final CanvasKitVariant variant;

static _CanvasKitVariantUrl chromium = _CanvasKitVariantUrl(
'$_canvasKitBaseUrl$_kChromiumCanvasKitJsFileName',
CanvasKitVariant.chromium,
);

static _CanvasKitVariantUrl full = _CanvasKitVariantUrl(
'$_canvasKitBaseUrl$_kFullCanvasKitJsFileName',
CanvasKitVariant.full,
);
}

List<_CanvasKitVariantUrl> get _canvasKitUrls {
String get _canvasKitBaseUrl => configuration.canvasKitBaseUrl;
List<String> get _canvasKitJsFileNames {
switch (configuration.canvasKitVariant) {
case CanvasKitVariant.auto:
return <_CanvasKitVariantUrl>[
if (browserSupportsCanvaskitChromium) _CanvasKitVariantUrl.chromium,
_CanvasKitVariantUrl.full,
return <String>[
if (_enableCanvasKitChromiumInAutoMode) _kChromiumCanvasKitJsFileName,
_kFullCanvasKitJsFileName,
];
case CanvasKitVariant.full:
return <_CanvasKitVariantUrl>[_CanvasKitVariantUrl.full];
return <String>[_kFullCanvasKitJsFileName];
case CanvasKitVariant.chromium:
return <_CanvasKitVariantUrl>[_CanvasKitVariantUrl.chromium];
return <String>[_kChromiumCanvasKitJsFileName];
}
}

Iterable<String> get _canvasKitJsUrls {
return _canvasKitJsFileNames.map((String filename) => '$_canvasKitBaseUrl$filename');
}
@visibleForTesting
String canvasKitWasmModuleUrl(String file, String canvasKitBase) =>
canvasKitBase + file;
Expand All @@ -2749,23 +2723,29 @@ String canvasKitWasmModuleUrl(String file, String canvasKitBase) =>
/// Downloads the CanvasKit JavaScript, then calls `CanvasKitInit` to download
/// and intialize the CanvasKit wasm.
Future<CanvasKit> downloadCanvasKit() async {
await _downloadOneOf(_canvasKitUrls);
await _downloadOneOf(_canvasKitJsUrls);

return CanvasKitInit(CanvasKitInitOptions(
final CanvasKit canvasKit = await CanvasKitInit(CanvasKitInitOptions(
locateFile: allowInterop(canvasKitWasmModuleUrl),
));

if (canvasKit.ParagraphBuilder.RequiresClientICU() && !browserSupportsCanvaskitChromium) {
throw Exception(
'The CanvasKit variant you are using only works on Chromium browsers. '
'Please use a different CanvasKit variant, or use a Chromium browser.',
);
}

return canvasKit;
}

/// Finds the first entry in [urls] that can be downloaded successfully, and
/// Finds the first URL in [urls] that can be downloaded successfully, and
/// downloads it.
///
/// If none of the URLs can be downloaded, throws an [Exception].
///
/// Also sets [canvasKitVariant] to the variant of CanvasKit that was downloaded.
Future<void> _downloadOneOf(Iterable<_CanvasKitVariantUrl> urls) async {
for (final _CanvasKitVariantUrl entry in urls) {
if (await _downloadCanvasKitJs(entry.url)) {
canvasKitVariant = entry.variant;
Future<void> _downloadOneOf(Iterable<String> urls) async {
for (final String url in urls) {
if (await _downloadCanvasKitJs(url)) {
return;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/canvaskit/text.dart
Original file line number Diff line number Diff line change
Expand Up @@ -984,7 +984,7 @@ class CkParagraphBuilder implements ui.ParagraphBuilder {

/// Builds the CkParagraph with the builder and deletes the builder.
SkParagraph _buildSkParagraph() {
if (canvasKitVariant == CanvasKitVariant.chromium) {
if (canvasKit.ParagraphBuilder.RequiresClientICU()) {
injectClientICU(_paragraphBuilder);
}
final SkParagraph result = _paragraphBuilder.build();
Expand Down
3 changes: 1 addition & 2 deletions lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,14 @@ import 'dart:typed_data';
import '../dom.dart';
import '../text/line_breaker.dart';
import 'canvaskit_api.dart';
import 'renderer.dart';

/// Injects required ICU data into the [builder].
///
/// This should only be used with the CanvasKit Chromium variant that's compiled
/// without ICU data.
void injectClientICU(SkParagraphBuilder builder) {
assert(
canvasKitVariant == CanvasKitVariant.chromium,
canvasKit.ParagraphBuilder.RequiresClientICU(),
'This method should only be used with the CanvasKit Chromium variant.',
);

Expand Down
4 changes: 2 additions & 2 deletions lib/web_ui/test/canvaskit/canvaskit_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1624,7 +1624,7 @@ void _paragraphTests() {
builder.pushStyle(
canvasKit.TextStyle(SkTextStyleProperties()..halfLeading = true));
builder.pop();
if (canvasKitVariant == CanvasKitVariant.chromium) {
if (canvasKit.ParagraphBuilder.RequiresClientICU()) {
injectClientICU(builder);
}
final SkParagraph paragraph = builder.build();
Expand Down Expand Up @@ -1742,7 +1742,7 @@ void _paragraphTests() {
);
builder.addText('hello');

if (canvasKitVariant == CanvasKitVariant.chromium) {
if (canvasKit.ParagraphBuilder.RequiresClientICU()) {
injectClientICU(builder);
}

Expand Down