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

[web] Fix canvas2d leaks in text measurement #38640

Merged
merged 5 commits into from
Jan 6, 2023
Merged
Changes from 1 commit
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
27 changes: 18 additions & 9 deletions lib/web_ui/lib/src/engine/text/layout_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ import 'paragraph.dart';
import 'ruler.dart';
import 'text_direction.dart';

/// A single canvas2d context to use for all text measurements.
final DomCanvasRenderingContext2D _context = _createTextMeasurementCanvas();

DomCanvasRenderingContext2D _createTextMeasurementCanvas() {
final DomCanvasElement canvas = createDomCanvasElement();
// We don't use this canvas to draw anything, so let's make it as small as
// possible to save memory.
canvas
..width = 0
..height = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should be able to test that we use a 0x0 canvas.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a test that checks for number of canvases created as well as the size.

return canvas.context2D;
}

/// Performs layout on a [CanvasParagraph].
///
/// It uses a [DomCanvasElement] to measure text.
Expand All @@ -24,9 +37,6 @@ class TextLayoutService {

final CanvasParagraph paragraph;

final DomCanvasRenderingContext2D context =
createDomCanvasElement().context2D;

// *** Results of layout *** //

// Look at the Paragraph class for documentation of the following properties.
Expand All @@ -53,7 +63,7 @@ class TextLayoutService {
ui.Rect get paintBounds => _paintBounds;
ui.Rect _paintBounds = ui.Rect.zero;

late final Spanometer spanometer = Spanometer(paragraph, context);
late final Spanometer spanometer = Spanometer(paragraph);

late final LayoutFragmenter layoutFragmenter =
LayoutFragmenter(paragraph.plainText, paragraph.spans);
Expand Down Expand Up @@ -882,10 +892,9 @@ class LineBuilder {
/// it's set, the [Spanometer] updates the underlying [context] so that
/// subsequent measurements use the correct styles.
class Spanometer {
Spanometer(this.paragraph, this.context);
Spanometer(this.paragraph);

final CanvasParagraph paragraph;
final DomCanvasRenderingContext2D context;

static final RulerHost _rulerHost = RulerHost();

Expand Down Expand Up @@ -938,7 +947,7 @@ class Spanometer {
final String cssFontString = span.style.cssFontString;
if (_cssFontString != cssFontString) {
_cssFontString = cssFontString;
context.font = cssFontString;
_context.font = cssFontString;
}
}

Expand All @@ -955,7 +964,7 @@ class Spanometer {
double get height => _currentRuler!.height;

double measureText(String text) {
return measureSubstring(context, text, 0, text.length);
return measureSubstring(_context, text, 0, text.length);
}

double measureRange(int start, int end) {
Expand Down Expand Up @@ -1047,7 +1056,7 @@ class Spanometer {
assert(end >= currentSpan.start && end <= currentSpan.end);

return measureSubstring(
context,
_context,
paragraph.plainText,
start,
end,
Expand Down