Skip to content

Commit

Permalink
[flutter_releases] Flutter stable 2.10.1 Engine Cherrypicks (#31339)
Browse files Browse the repository at this point in the history
* 'Update Dart SDK to 0180af250ff518cc0fa494a4eb484ce11ec1e62c'

* [Web Text Input] ensure the input element is put in the DOM on desktop safari (#30885)

* Reland: Teardown external view embedder prior to unmerging threads (#31122)

* Don't remove views while the rasterizer is torn down (#31229)

* [web] roll CanvasKit 0.32.0; fix frame order in animated images (#30680)

* roll CanvasKit 0.32.0
* Fix frame order in WASM image codecs

* [web] roll CanvasKit to 0.33.0 (#31240)

* 'add branch flutter-2.8-candidate.16 to enabled_branches in .ci.yaml'

* remove candidate from enabled_branches

* resolved merge conflict in DEPS file for the canvaskit_cipd_instance key

* update license hash

* resolving presubmit failures on android tests

Co-authored-by: Kevin Chisholm <kevinjchisholm@google.com>
Co-authored-by: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com>
Co-authored-by: Emmanuel Garcia <egarciad@google.com>
Co-authored-by: Yegor <yjbanov@google.com>
Co-authored-by: eggfly <lihaohua.cafebabe@bytedance.com>
  • Loading branch information
6 people committed Feb 9, 2022
1 parent 776efd2 commit ab46186
Show file tree
Hide file tree
Showing 24 changed files with 277 additions and 140 deletions.
4 changes: 2 additions & 2 deletions DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ vars = {

# WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY
# See `lib/web_ui/README.md` for how to roll CanvasKit to a new version.
'canvaskit_cipd_instance': 'NcwvqeeKK7urddCbEdDvHytdaCiCA_8-4oS_T_ouGfgC',
'canvaskit_cipd_instance': '8MSYGWVWzrTJIoVL00ZquruZs-weuwLBy1kt1AawJiIC',

# When updating the Dart revision, ensure that all entries that are
# dependencies of Dart are also updated to match the entries in the
# Dart SDK's DEPS file for that revision of Dart. The DEPS file for
# Dart is: https://github.com/dart-lang/sdk/blob/main/DEPS.
# You can use //tools/dart/create_updated_flutter_deps.py to produce
# updated revision list of existing dependencies.
'dart_revision': '547d54e13cc8c1ce9279792fd16c189663e18f96',
'dart_revision': '0180af250ff518cc0fa494a4eb484ce11ec1e62c',

# WARNING: DO NOT EDIT MANUALLY
# The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py
Expand Down
2 changes: 1 addition & 1 deletion ci/licenses_golden/licenses_third_party
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Signature: 51c303e95a17cde2eb9ee7806c16a1c5
Signature: cc2abaf0233d38199643282d27336c28

UNUSED LICENSES:

Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/dev/canvaskit_lock.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Specifies the version of CanvasKit to use for Flutter Web apps.
#
# See `lib/web_ui/README.md` for how to update this file.
canvaskit_version: "0.31.0"
canvaskit_version: "0.33.0"
7 changes: 6 additions & 1 deletion lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -758,9 +758,14 @@ class SkColorType {
class SkAnimatedImage {
external int getFrameCount();

/// Returns duration in milliseconds.
external int getRepetitionCount();

/// Returns duration in milliseconds.
external int currentFrameDuration();

/// Advances to the next frame and returns its duration in milliseconds.
external int decodeNextFrame();

external SkImage makeImageAtCurrentFrame();
external int width();
external int height();
Expand Down
38 changes: 28 additions & 10 deletions lib/web_ui/lib/src/engine/canvaskit/image_wasm_codecs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
int _frameCount = 0;
int _repetitionCount = -1;

/// The index to the next frame to be decoded.
int _nextFrameIndex = 0;
/// Current frame index.
int _currentFrameIndex = 0;

@override
SkAnimatedImage createDefault() {
Expand All @@ -48,11 +48,16 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
_frameCount = animatedImage.getFrameCount();
_repetitionCount = animatedImage.getRepetitionCount();

// If the object has been deleted then resurrected, it may already have
// iterated over some frames. We need to skip over them.
for (int i = 0; i < _nextFrameIndex; i++) {
// Normally CanvasKit initializes `SkAnimatedImage` to point to the first
// frame in the animation. However, if the Skia object has been deleted then
// resurrected, the framework/app may already have advanced to one of the
// subsequent frames. When that happens the value of _currentFrameIndex will
// be something other than zero, and we need to tell the decoder to skip
// over the previous frames to point to the current one.
for (int i = 0; i < _currentFrameIndex; i++) {
animatedImage.decodeNextFrame();
}

return animatedImage;
}

Expand Down Expand Up @@ -100,10 +105,23 @@ class CkAnimatedImage extends ManagedSkiaObject<SkAnimatedImage>
@override
Future<ui.FrameInfo> getNextFrame() {
assert(_debugCheckIsNotDisposed());
final int durationMillis = skiaObject.decodeNextFrame();
final Duration duration = Duration(milliseconds: durationMillis);
final CkImage image = CkImage(skiaObject.makeImageAtCurrentFrame());
_nextFrameIndex = (_nextFrameIndex + 1) % _frameCount;
return Future<ui.FrameInfo>.value(AnimatedImageFrameInfo(duration, image));
final SkAnimatedImage animatedImage = skiaObject;

// SkAnimatedImage comes pre-initialized to point to the current frame (by
// default the first frame, and, with some special resurrection logic in
// `createDefault`, to a subsequent frame if resurrection happens in the
// middle of animation). Flutter's `Codec` semantics is to initialize to
// point to "just before the first frame", i.e. the first invocation of
// `getNextFrame` returns the first frame. Therefore, we have to read the
// current Skia frame, then advance SkAnimatedImage to the next frame, and
// return the current frame.
final ui.FrameInfo currentFrame = AnimatedImageFrameInfo(
Duration(milliseconds: animatedImage.currentFrameDuration()),
CkImage(animatedImage.makeImageAtCurrentFrame()),
);

animatedImage.decodeNextFrame();
_currentFrameIndex = (_currentFrameIndex + 1) % _frameCount;
return Future<ui.FrameInfo>.value(currentFrame);
}
}
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import 'package:js/js.dart';
/// The version of CanvasKit used by the web engine by default.
// DO NOT EDIT THE NEXT LINE OF CODE MANUALLY
// See `lib/web_ui/README.md` for how to roll CanvasKit to a new version.
const String _canvaskitVersion = '0.31.0';
const String _canvaskitVersion = '0.33.0';

/// The Web Engine configuration for the current application.
FlutterConfiguration get configuration => _configuration ??= FlutterConfiguration(_jsConfiguration);
Expand Down
10 changes: 7 additions & 3 deletions lib/web_ui/lib/src/engine/text_editing/text_editing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -782,9 +782,10 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy {
// On Safari Desktop, when a form is focused, it opens an autofill menu
// immediately.
// Flutter framework sends `setEditableSizeAndTransform` for informing
// the engine about the location of the text field. This call will
// arrive after `show` call. Therefore form is placed, when
// `setEditableSizeAndTransform` method is called and focus called on the
// the engine about the location of the text field. This call may arrive
// after the first `show` call, depending on the text input widget's
// implementation. Therefore form is placed, when
// `setEditableSizeAndTransform` method is called and focus called on the
// form only after placing it to the correct position and only once after
// that. Calling focus multiple times causes flickering.
focusedFormElement!.focus();
Expand All @@ -800,6 +801,9 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy {

@override
void initializeElementPlacement() {
if (geometry != null) {
placeElement();
}
activeDomElement.focus();
}
}
Expand Down
9 changes: 4 additions & 5 deletions lib/web_ui/test/canvaskit/image_golden_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,17 @@ void _testForImageCodecs({required bool useBrowserImageDecoder}) {
expect(image.repetitionCount, -1);

final ui.FrameInfo frame1 = await image.getNextFrame();
await expectFrameData(frame1, <int>[0, 255, 0, 255]);
await expectFrameData(frame1, <int>[255, 0, 0, 255]);
final ui.FrameInfo frame2 = await image.getNextFrame();
await expectFrameData(frame2, <int>[0, 0, 255, 255]);
await expectFrameData(frame2, <int>[0, 255, 0, 255]);

// Pretend that the image is temporarily deleted.
image.delete();
image.didDelete();

// Check that we got the 3rd frame after resurrection.
final ui.FrameInfo frame3 = await image.getNextFrame();
await expectFrameData(frame3, <int>[255, 0, 0, 255]);
await expectFrameData(frame3, <int>[0, 0, 255, 255]);

testCollector.collectNow();
});
Expand Down Expand Up @@ -548,11 +548,10 @@ void _testCkAnimatedImage() {

test('CkAnimatedImage toByteData(RGBA)', () async {
final CkAnimatedImage image = CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test');
// TODO(yjbanov): frame sequence is wrong (https://github.com/flutter/flutter/issues/95281)
const List<List<int>> expectedColors = <List<int>>[
<int>[255, 0, 0, 255],
<int>[0, 255, 0, 255],
<int>[0, 0, 255, 255],
<int>[255, 0, 0, 255],
];
for (int i = 0; i < image.frameCount; i++) {
final ui.FrameInfo frame = await image.getNextFrame();
Expand Down
34 changes: 34 additions & 0 deletions lib/web_ui/test/text_editing_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,40 @@ void testMain() {
expect(spy.messages, isEmpty);
});

test('setClient, setEditingState, setSizeAndTransform, show - input element is put into the DOM', () {
editingStrategy = SafariDesktopTextEditingStrategy(textEditing!);
textEditing!.debugTextEditingStrategyOverride = editingStrategy;
final MethodCall setClient = MethodCall(
'TextInput.setClient', <dynamic>[123, flutterSinglelineConfig]);
sendFrameworkMessage(codec.encodeMethodCall(setClient));

const MethodCall setEditingState =
MethodCall('TextInput.setEditingState', <String, dynamic>{
'text': 'abcd',
'selectionBase': 2,
'selectionExtent': 3,
});
sendFrameworkMessage(codec.encodeMethodCall(setEditingState));

// Editing shouldn't have started yet.
expect(document.activeElement, document.body);

// The "setSizeAndTransform" message has to be here before we call
// checkInputEditingState, since on some platforms (e.g. Desktop Safari)
// we don't put the input element into the DOM until we get its correct
// dimensions from the framework.
final MethodCall setSizeAndTransform =
configureSetSizeAndTransformMethodCall(150, 50,
Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList());
sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform));

const MethodCall show = MethodCall('TextInput.show');
sendFrameworkMessage(codec.encodeMethodCall(show));

expect(defaultTextEditingRoot.activeElement,
textEditing!.strategy.domElement);
});

test('setClient, setEditingState, show, updateConfig, clearClient', () {
final MethodCall setClient = MethodCall('TextInput.setClient', <dynamic>[
123,
Expand Down
10 changes: 6 additions & 4 deletions shell/common/rasterizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ void Rasterizer::Setup(std::unique_ptr<Surface> surface) {
}
}

void Rasterizer::TeardownExternalViewEmbedder() {
if (external_view_embedder_) {
external_view_embedder_->Teardown();
}
}

void Rasterizer::Teardown() {
auto context_switch =
surface_ ? surface_->MakeRenderContextCurrent() : nullptr;
Expand All @@ -97,10 +103,6 @@ void Rasterizer::Teardown() {
raster_thread_merger_->UnMergeNowIfLastOne();
raster_thread_merger_->SetMergeUnmergeCallback(nullptr);
}

if (external_view_embedder_) {
external_view_embedder_->Teardown();
}
}

void Rasterizer::EnableThreadMergerIfNeeded() {
Expand Down
7 changes: 7 additions & 0 deletions shell/common/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ class Rasterizer final : public SnapshotDelegate {
///
void Teardown();

//----------------------------------------------------------------------------
/// @brief Releases any resource used by the external view embedder.
/// For example, overlay surfaces or Android views.
/// On Android, this method post a task to the platform thread,
/// and waits until it completes.
void TeardownExternalViewEmbedder();

//----------------------------------------------------------------------------
/// @brief Notifies the rasterizer that there is a low memory situation
/// and it must purge as many unnecessary resources as possible.
Expand Down
5 changes: 5 additions & 0 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,11 @@ void Shell::OnPlatformViewDestroyed() {
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetRasterTaskRunner(),
raster_task);
latch.Wait();
// On Android, the external view embedder posts a task to the platform thread,
// and waits until it completes.
// As a result, the platform thread must not be blocked prior to calling
// this method.
rasterizer_->TeardownExternalViewEmbedder();
}

// |PlatformView::Delegate|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

#include "flutter/shell/platform/android/external_view_embedder/external_view_embedder.h"

#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/fml/task_runner.h"
#include "flutter/fml/trace_event.h"
#include "flutter/shell/platform/android/surface/android_surface.h"

Expand All @@ -12,12 +14,14 @@ namespace flutter {
AndroidExternalViewEmbedder::AndroidExternalViewEmbedder(
const AndroidContext& android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
std::shared_ptr<AndroidSurfaceFactory> surface_factory)
std::shared_ptr<AndroidSurfaceFactory> surface_factory,
TaskRunners task_runners)
: ExternalViewEmbedder(),
android_context_(android_context),
jni_facade_(jni_facade),
surface_factory_(surface_factory),
surface_pool_(std::make_unique<SurfacePool>()) {}
surface_pool_(std::make_unique<SurfacePool>()),
task_runners_(task_runners) {}

// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView(
Expand Down Expand Up @@ -264,8 +268,8 @@ void AndroidExternalViewEmbedder::BeginFrame(

// The surface size changed. Therefore, destroy existing surfaces as
// the existing surfaces in the pool can't be recycled.
if (frame_size_ != frame_size && raster_thread_merger->IsOnPlatformThread()) {
surface_pool_->DestroyLayers(jni_facade_);
if (frame_size_ != frame_size) {
DestroySurfaces();
}
surface_pool_->SetFrameSize(frame_size);
// JNI method must be called on the platform thread.
Expand Down Expand Up @@ -300,7 +304,18 @@ bool AndroidExternalViewEmbedder::SupportsDynamicThreadMerging() {

// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder::Teardown() {
surface_pool_->DestroyLayers(jni_facade_);
DestroySurfaces();
}

// |ExternalViewEmbedder|
void AndroidExternalViewEmbedder::DestroySurfaces() {
fml::AutoResetWaitableEvent latch;
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetPlatformTaskRunner(),
[&]() {
surface_pool_->DestroyLayers(jni_facade_);
latch.Signal();
});
latch.Wait();
}

} // namespace flutter
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <unordered_map>

#include "flutter/common/task_runners.h"
#include "flutter/flow/embedded_views.h"
#include "flutter/flow/rtree.h"
#include "flutter/shell/platform/android/context/android_context.h"
Expand All @@ -32,7 +33,8 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
AndroidExternalViewEmbedder(
const AndroidContext& android_context,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
std::shared_ptr<AndroidSurfaceFactory> surface_factory);
std::shared_ptr<AndroidSurfaceFactory> surface_factory,
TaskRunners task_runners);

// |ExternalViewEmbedder|
void PrerollCompositeEmbeddedView(
Expand Down Expand Up @@ -99,6 +101,9 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
// Holds surfaces. Allows to recycle surfaces or allocate new ones.
const std::unique_ptr<SurfacePool> surface_pool_;

// The task runners.
const TaskRunners task_runners_;

// The size of the root canvas.
SkISize frame_size_;

Expand Down Expand Up @@ -126,6 +131,11 @@ class AndroidExternalViewEmbedder final : public ExternalViewEmbedder {
// The number of platform views in the previous frame.
int64_t previous_frame_view_count_;

// Destroys the surfaces created from the surface factory.
// This method schedules a task on the platform thread, and waits for
// the task until it completes.
void DestroySurfaces();

// Resets the state.
void Reset();

Expand Down

0 comments on commit ab46186

Please sign in to comment.