未验证 提交 371a8a05 编写于 作者: F Ferhat 提交者: GitHub

[web] When a canvas element is reused and is first element in child list, preserve zIndex. (#17906)

* Fix zIndex removal on reuse
* Move check after append call to simplify
上级 af19ea7b
......@@ -80,12 +80,10 @@ class _CanvasPool extends _SaveStackTracking {
void _createCanvas() {
bool requiresClearRect = false;
bool reused = false;
if (_reusablePool != null && _reusablePool.isNotEmpty) {
_canvas = _reusablePool.removeAt(0);
// If a canvas is the first element we set z-index = -1 to workaround
// blink compositing bug. To make sure this does not leak when reused
// reset z-index.
_canvas.style.removeProperty('z-index');
reused = true;
requiresClearRect = true;
} else {
// Compute the final CSS canvas size given the actual pixel count we
......@@ -116,6 +114,12 @@ class _CanvasPool extends _SaveStackTracking {
..height = '${cssHeight}px';
}
// Before appending canvas, check if canvas is already on rootElement. This
// optimization prevents DOM .append call when a PersistentSurface is
// reused. Reading lastChild is faster than append call.
if (_rootElement.lastChild != _canvas) {
_rootElement.append(_canvas);
}
// When the picture has a 90-degree transform and clip in its
// ancestor layers, it triggers a bug in Blink and Webkit browsers
// that results in canvas obscuring text that should be painted on
......@@ -126,16 +130,15 @@ class _CanvasPool extends _SaveStackTracking {
// Possible Blink bugs that are causing this:
// * https://bugs.chromium.org/p/chromium/issues/detail?id=370604
// * https://bugs.chromium.org/p/chromium/issues/detail?id=586601
final bool isFirstChildElement = _rootElement.firstChild == null;
if (isFirstChildElement) {
if (_rootElement.firstChild == _canvas) {
_canvas.style.zIndex = '-1';
} else if (reused) {
// If a canvas is the first element we set z-index = -1 to workaround
// blink compositing bug. To make sure this does not leak when reused
// reset z-index.
_canvas.style.removeProperty('z-index');
}
// Before appending canvas, check if canvas is already on rootElement. This
// optimization prevents DOM .append call when a PersistentSurface is
// reused. Reading lastChild is faster than append call.
if (_rootElement.lastChild != _canvas) {
_rootElement.append(_canvas);
}
_context = _canvas.context2D;
_contextHandle = ContextStateHandle(_context);
_initializeViewport(requiresClearRect);
......
......@@ -177,6 +177,39 @@ void main() {
}, // TODO(nurhan): https://github.com/flutter/flutter/issues/46638
skip: (browserEngine == BrowserEngine.firefox));
});
group('Compositing order', () {
// Regression test for https://github.com/flutter/flutter/issues/55058
//
// When BitmapCanvas uses multiple elements to paint, the very first
// canvas needs to have a -1 zIndex so it can preserve compositing order.
test('First canvas element should retain -1 zIndex after update', () async {
final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
final Picture picture1 = _drawPicture();
EngineLayer oldLayer = builder.pushClipRect(
const Rect.fromLTRB(10, 10, 300, 300),
);
builder.addPicture(Offset.zero, picture1);
builder.pop();
html.HtmlElement content = builder.build().webOnlyRootElement;
expect(content.querySelector('canvas').style.zIndex, '-1');
// Force update to scene which will utilize reuse code path.
final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder();
builder2.pushClipRect(
const Rect.fromLTRB(5, 10, 300, 300),
oldLayer: oldLayer
);
final Picture picture2 = _drawPicture();
builder2.addPicture(Offset.zero, picture2);
builder2.pop();
html.HtmlElement contentAfterReuse = builder2.build().webOnlyRootElement;
expect(contentAfterReuse.querySelector('canvas').style.zIndex, '-1');
});
});
}
typedef TestLayerBuilder = EngineLayer Function(
......@@ -312,3 +345,32 @@ class MockPersistedPicture extends PersistedPicture {
@override
int get bitmapPixelCount => 0;
}
Picture _drawPicture() {
const double offsetX = 50;
const double offsetY = 50;
final EnginePictureRecorder recorder = PictureRecorder();
final RecordingCanvas canvas =
recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400));
canvas.drawCircle(
Offset(offsetX + 10, offsetY + 10), 10, Paint()..style = PaintingStyle.fill);
canvas.drawCircle(
Offset(offsetX + 60, offsetY + 10),
10,
Paint()
..style = PaintingStyle.fill
..color = const Color.fromRGBO(255, 0, 0, 1));
canvas.drawCircle(
Offset(offsetX + 10, offsetY + 60),
10,
Paint()
..style = PaintingStyle.fill
..color = const Color.fromRGBO(0, 255, 0, 1));
canvas.drawCircle(
Offset(offsetX + 60, offsetY + 60),
10,
Paint()
..style = PaintingStyle.fill
..color = const Color.fromRGBO(0, 0, 255, 1));
return recorder.endRecording();
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册