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

Commit 2e0616a

Browse files
[Skwasm] Correctly handle paragraphs with empty text. (#51695)
Instead of just returning, if our paragraph builder has empty text, we still need to generate a set of line breaks for skia to use. Otherwise, it will actually cause subtle memory access errors.
1 parent 8389c50 commit 2e0616a

File tree

3 files changed

+31
-19
lines changed

3 files changed

+31
-19
lines changed

lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -940,27 +940,29 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper<RawParagraphBuilder> im
940940
// just create both up front here.
941941
final Pointer<Uint32> outSize = scope.allocUint32Array(1);
942942
final Pointer<Uint8> utf8Data = paragraphBuilderGetUtf8Text(handle, outSize);
943+
944+
final String text;
945+
final JSString jsText;
943946
if (utf8Data == nullptr) {
944-
return;
947+
text = '';
948+
jsText = ''.toJS;
949+
} else {
950+
final List<int> codeUnitList = List<int>.generate(
951+
outSize.value,
952+
(int index) => utf8Data[index]
953+
);
954+
text = utf8.decode(codeUnitList);
955+
jsText = _utf8Decoder.decode(
956+
// In an ideal world we would just use a subview of wasm memory rather
957+
// than a slice, but the TextDecoder API doesn't work on shared buffer
958+
// sources yet.
959+
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1012656
960+
createUint8ArrayFromBuffer(skwasmInstance.wasmMemory.buffer).slice(
961+
utf8Data.address.toJS,
962+
(utf8Data.address + outSize.value).toJS
963+
));
945964
}
946965

947-
// TODO(jacksongardner): We could make a subclass of `List<int>` here to
948-
// avoid this copy.
949-
final List<int> codeUnitList = List<int>.generate(
950-
outSize.value,
951-
(int index) => utf8Data[index]
952-
);
953-
final String text = utf8.decode(codeUnitList);
954-
final JSString jsText = _utf8Decoder.decode(
955-
// In an ideal world we would just use a subview of wasm memory rather
956-
// than a slice, but the TextDecoder API doesn't work on shared buffer
957-
// sources yet.
958-
// See https://bugs.chromium.org/p/chromium/issues/detail?id=1012656
959-
createUint8ArrayFromBuffer(skwasmInstance.wasmMemory.buffer).slice(
960-
utf8Data.address.toJS,
961-
(utf8Data.address + outSize.value).toJS
962-
));
963-
964966
_addGraphemeBreakData(text, jsText);
965967
_addWordBreakData(text, jsText);
966968
_addLineBreakData(text, jsText);

lib/web_ui/test/ui/line_metrics_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,5 +176,5 @@ Future<void> testMain() async {
176176

177177
// In Roboto, the width should be 11 here. In the test font, it would be square (16 points)
178178
expect(metrics!.width, 11);
179-
}, solo: true);
179+
});
180180
}

lib/web_ui/test/ui/paragraph_builder_test.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,14 @@ Future<void> testMain() async {
3939

4040
expect(() => builder.build(), returnsNormally);
4141
});
42+
43+
test('build and layout a paragraph with an empty addText', () {
44+
final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle());
45+
builder.addText('');
46+
final Paragraph paragraph = builder.build();
47+
expect(
48+
() => paragraph.layout(const ParagraphConstraints(width: double.infinity)),
49+
returnsNormally,
50+
);
51+
});
4252
}

0 commit comments

Comments
 (0)