From 4a8f901fcdf7322588e125a776d0bf072c2c18f1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 14 Mar 2020 22:46:02 -0400 Subject: [PATCH 001/386] Roll src/third_party/skia 320c32bfa306..190b6e54b016 (1 commits) (#17155) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 92e3f4f7dfb4e..399bd4cb24873 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '320c32bfa3062b0c67270667df5e4df1856f176e', + 'skia_revision': '190b6e54b016c4925e414ecbc37cc2821d0340dc', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2020d0fbc8a06..06b85b1dffc95 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: bc22be57e83e2768786b3add6df2436a +Signature: f2e3266890e53b91e21959c54e933f22 UNUSED LICENSES: @@ -3086,7 +3086,6 @@ FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.cpp FILE: ../../../third_party/skia/src/core/SkStrikeForGPU.h FILE: ../../../third_party/skia/src/core/SkStrikeSpec.h FILE: ../../../third_party/skia/src/core/SkVMBlitter.cpp -FILE: ../../../third_party/skia/src/core/SkVMBlitter.h FILE: ../../../third_party/skia/src/core/SkYUVMath.cpp FILE: ../../../third_party/skia/src/core/SkYUVMath.h FILE: ../../../third_party/skia/src/core/SkZip.h From 1f368293c30120e777fede20b09076daf495c66f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 14 Mar 2020 23:46:02 -0400 Subject: [PATCH 002/386] Roll src/third_party/dart 7c5059f52881..13f1a4f4bf58 (14 commits) (#17156) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 399bd4cb24873..27e61cf7009f1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '7c5059f528814e14dc9ff29ee1629b922daf77a4', + 'dart_revision': '13f1a4f4bf589feea34f058845f3657b15db6ceb', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 623596773cb06..4945572b96ae1 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: b2a998d040384765d4bf2b5aa6e318ef +Signature: aeb67aa32318e9bbd3b7b420ac0760c9 UNUSED LICENSES: @@ -8281,6 +8281,7 @@ FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/js_array.d FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/js_number.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/js_string.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/math_patch.dart +FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/mirrors_patch_cfe.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/native_helper.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/regexp_helper.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/string_helper.dart From 9c8e4fcd8571b522ba09826512832b2ba72a9363 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 02:06:01 -0400 Subject: [PATCH 003/386] Roll fuchsia/sdk/core/mac-amd64 from eGWV8... to v_ZuR... (#17157) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 27e61cf7009f1..69d76714dec6b 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'eGWV8Up-Giv-15K1FBRnkFQompVx2GpVBNomG09my_wC' + 'version': 'v_ZuRNdwydW2YF1Xh78aYKlqQOPP328sC-8eoR3jcaMC' } ], 'condition': 'host_os == "mac"', From ab5cce503795905db03375bea8759238e4b42a53 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 05:26:02 -0400 Subject: [PATCH 004/386] Roll src/third_party/dart 13f1a4f4bf58..eff5548a684a (1 commits) (#17158) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 69d76714dec6b..f57784478dbb1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '13f1a4f4bf589feea34f058845f3657b15db6ceb', + 'dart_revision': 'eff5548a684a37239ed009a59dc75e1c1542b1a2', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 9cb2fdba76983ad66adbb1913926920136523c92 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 11:31:01 -0400 Subject: [PATCH 005/386] Roll src/third_party/skia 190b6e54b016..6352f00c59e4 (1 commits) (#17159) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f57784478dbb1..cb1929aa9fbef 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '190b6e54b016c4925e414ecbc37cc2821d0340dc', + 'skia_revision': '6352f00c59e411baca96c852c0f27a96ea6f920a', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 06b85b1dffc95..e3605ad69bc24 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f2e3266890e53b91e21959c54e933f22 +Signature: 7a2030f18fdaa1aa13e48ca7e3cb77af UNUSED LICENSES: From f0e1f77929f71c9bcfff49b27eabdb532348744d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 14:31:01 -0400 Subject: [PATCH 006/386] Roll src/third_party/skia 6352f00c59e4..7e0a7dbd22e0 (1 commits) (#17160) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index cb1929aa9fbef..d4a5b43abc2c9 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6352f00c59e411baca96c852c0f27a96ea6f920a', + 'skia_revision': '7e0a7dbd22e09e6537ebf19e60119f5800988bbd', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index e3605ad69bc24..b557423e9a801 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 7a2030f18fdaa1aa13e48ca7e3cb77af +Signature: f2dbd7bf15d428460cc9f1d3cdb8a5cc UNUSED LICENSES: From b23138c3598e0436bca7d2426512a72eea5df12b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 17:01:02 -0400 Subject: [PATCH 007/386] Roll fuchsia/sdk/core/mac-amd64 from v_ZuR... to T6G99... (#17161) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d4a5b43abc2c9..c784ffc065b2d 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'v_ZuRNdwydW2YF1Xh78aYKlqQOPP328sC-8eoR3jcaMC' + 'version': 'T6G99q6CStb2BeLVpWdxfsQNHt9shj9zQq_Ed-i9XTQC' } ], 'condition': 'host_os == "mac"', From d8e45f9c14eb19b8d2c0701aa2a0967a2ce8f7bc Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 15 Mar 2020 14:35:48 -0700 Subject: [PATCH 008/386] Update the Metal backend selection to use iOS 10.0 and A7 as the version floor. (#17162) Fixes https://github.com/flutter/flutter/issues/52356 --- .../darwin/ios/rendering_api_selection.mm | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/shell/platform/darwin/ios/rendering_api_selection.mm b/shell/platform/darwin/ios/rendering_api_selection.mm index c6ef89c47cc24..91b483f1deb3a 100644 --- a/shell/platform/darwin/ios/rendering_api_selection.mm +++ b/shell/platform/darwin/ios/rendering_api_selection.mm @@ -7,6 +7,9 @@ #include #include #include +#if FLUTTER_SHELL_ENABLE_METAL +#include +#endif // FLUTTER_SHELL_ENABLE_METAL #include "flutter/fml/logging.h" @@ -16,6 +19,7 @@ bool ShouldUseSoftwareRenderer() { return [[[NSProcessInfo processInfo] arguments] containsObject:@"--force-software"]; } +#if FLUTTER_SHELL_ENABLE_METAL bool ShouldUseMetalRenderer() { // If there is a command line argument that says Metal should not be used, that takes precedence // over everything else. This allows disabling Metal on a per run basis to check for regressions @@ -31,11 +35,13 @@ bool ShouldUseMetalRenderer() { return true; } - // This is just a version we picked that is easy to support and has all necessary Metal features. + // Flutter supports Metal on all devices with Apple A7 SoC or above that have been update to or + // past iOS 10.0. The processor was selected as it is the first version at which Metal was + // supported. The iOS version floor was selected due to the availability of features used by Skia. bool ios_version_supports_metal = false; - // TODO(52356): Update this to be the version selected for release. - if (@available(iOS 11.0, *)) { - ios_version_supports_metal = true; + if (@available(iOS 10.0, *)) { + auto device = MTLCreateSystemDefaultDevice(); + ios_version_supports_metal = [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]; } // The application must opt-in by default to use Metal without command line flags. @@ -44,6 +50,7 @@ bool ShouldUseMetalRenderer() { return ios_version_supports_metal && application_opts_into_metal; } +#endif // FLUTTER_SHELL_ENABLE_METAL IOSRenderingAPI GetRenderingAPIForProcess() { #if TARGET_IPHONE_SIMULATOR From 7880cc75a53e68ebed33fbd366cc1372b94f49ea Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 17:46:02 -0400 Subject: [PATCH 009/386] Roll src/third_party/skia 7e0a7dbd22e0..9a4ac6e86586 (1 commits) (#17163) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c784ffc065b2d..e49e7ff352b44 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '7e0a7dbd22e09e6537ebf19e60119f5800988bbd', + 'skia_revision': '9a4ac6e86586a04ae61f48d6007b7ac03c96a35b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index b557423e9a801..3f2d4ba93b931 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f2dbd7bf15d428460cc9f1d3cdb8a5cc +Signature: 103bb02b5ee8be710bc28504e489f40a UNUSED LICENSES: From 7e6974ff0c1b3589417b77c8b88d70d7c8d668ca Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 19:16:04 -0400 Subject: [PATCH 010/386] Roll fuchsia/sdk/core/linux-amd64 from 6ds94... to S9xSp... (#17164) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e49e7ff352b44..032c12230b591 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': '6ds94xRmD_5jjJ9jn1pngltBcWUyduH4JF-PxklsD50C' + 'version': 'S9xSpScwMVX6dLgXfPA_NAZ9zdl7hgqvXZgxBlr6tzkC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 336439ae8a093..591543c6618ef 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 8f652d8bf0e8a2e1e3edcf50cf29c0e8 +Signature: e2ec6f495e5d7854d864d8a261e829df UNUSED LICENSES: @@ -534,7 +534,9 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channel/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channelcontrol/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.common/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/meta.json @@ -1532,7 +1534,9 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channel/channel.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channel/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channelcontrol/channelcontrol.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channelcontrol/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/constants.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/context.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/cookie.fidl @@ -2266,7 +2270,9 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.types/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.views/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channel/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update.channelcontrol/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.web/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.common/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.wlan.policy/meta.json @@ -3268,7 +3274,9 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/night_mode.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/events.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/keyboard.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/modifiers.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/update.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/url.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/auth.fidl FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/stream.h FILE: ../../../fuchsia/sdk/linux/pkg/zx/stream.cc ---------------------------------------------------------------------------------------------------- From 3e02f3bec91ea911a307e8e5519035576ba21fcc Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 21:11:01 -0400 Subject: [PATCH 011/386] Roll src/third_party/skia 9a4ac6e86586..276a78573e91 (1 commits) (#17166) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 032c12230b591..2f534a69b6092 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '9a4ac6e86586a04ae61f48d6007b7ac03c96a35b', + 'skia_revision': '276a78573e911a3e2af5a3efb3cfcac5884e056e', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 3f2d4ba93b931..f49fe8a4aca25 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 103bb02b5ee8be710bc28504e489f40a +Signature: 21419b143aaed373350ffc06693cd414 UNUSED LICENSES: From 6801b4dae791e458db94775d675fec1fb5efb15f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 15 Mar 2020 22:21:01 -0400 Subject: [PATCH 012/386] Roll src/third_party/dart eff5548a684a..7079c49b054e (1 commits) (#17165) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 2f534a69b6092..f8dbb8e13ddda 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'eff5548a684a37239ed009a59dc75e1c1542b1a2', + 'dart_revision': '7079c49b054e211b6efac6964574e65635e5da43', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 0078a6c6afcd69568eac8d6bacd9bdc36e7b2793 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 06:11:01 -0400 Subject: [PATCH 013/386] Roll src/third_party/dart 7079c49b054e..d2950737b8a2 (2 commits) (#17167) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f8dbb8e13ddda..7411f049ebbb2 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '7079c49b054e211b6efac6964574e65635e5da43', + 'dart_revision': 'd2950737b8a295664e9da8df3710722215e1cc1e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From a9d49a0f6ef52e0702cc2f2513ab1ce3a92eebe0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 07:56:01 -0400 Subject: [PATCH 014/386] Roll fuchsia/sdk/core/mac-amd64 from T6G99... to XRDIh... (#17168) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 7411f049ebbb2..24e4cc3e89387 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'T6G99q6CStb2BeLVpWdxfsQNHt9shj9zQq_Ed-i9XTQC' + 'version': 'XRDIh5zK_woqoYL547JcCDq3LLX6COTvXOoTDv7tXn0C' } ], 'condition': 'host_os == "mac"', From 032a8f311862adbf9816733ec9db2c3c93adff49 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 11:51:02 -0400 Subject: [PATCH 015/386] Roll src/third_party/skia 276a78573e91..16701eecd595 (5 commits) (#17170) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 24e4cc3e89387..2b478ca7d9486 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '276a78573e911a3e2af5a3efb3cfcac5884e056e', + 'skia_revision': '16701eecd5957e2d5eb0890eb2e9a72771d57941', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f49fe8a4aca25..4f880350a4930 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 21419b143aaed373350ffc06693cd414 +Signature: f0e65dedaa9fb3e395875da83961406d UNUSED LICENSES: From 733933ad5c68d72fd8edb35d93de6aabe3d57d36 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 12:21:03 -0400 Subject: [PATCH 016/386] Roll src/third_party/dart d2950737b8a2..d7a7854a5e98 (2 commits) (#17171) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2b478ca7d9486..c6b931097b884 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd2950737b8a295664e9da8df3710722215e1cc1e', + 'dart_revision': 'd7a7854a5e98d0d447a24db1688ec69a1f0104f7', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 4945572b96ae1..df99ab055766f 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: aeb67aa32318e9bbd3b7b420ac0760c9 +Signature: 9df43a93e8c8715e3373f456b338b59f UNUSED LICENSES: From fddb0c272e2f3e9f9639e1c1dbfcbf2ff86a262c Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 16 Mar 2020 11:00:03 -0700 Subject: [PATCH 017/386] Made it so you can whitelist what events you want to listen to (#17108) --- ci/licenses_golden/licenses_flutter | 3 + common/settings.h | 1 + fml/BUILD.gn | 3 + fml/ascii_trie.cc | 48 ++++++ fml/ascii_trie.h | 39 +++++ fml/ascii_trie_unittests.cc | 36 +++++ fml/trace_event.cc | 222 +++++++++++++++------------- fml/trace_event.h | 2 + shell/common/shell.cc | 16 ++ shell/common/switches.cc | 3 + shell/common/switches.h | 5 + 11 files changed, 279 insertions(+), 99 deletions(-) create mode 100644 fml/ascii_trie.cc create mode 100644 fml/ascii_trie.h create mode 100644 fml/ascii_trie_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ef7478a6914a0..0dac1de978ccc 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -107,6 +107,9 @@ FILE: ../../../flutter/flow/view_holder.cc FILE: ../../../flutter/flow/view_holder.h FILE: ../../../flutter/flutter_frontend_server/bin/starter.dart FILE: ../../../flutter/flutter_frontend_server/lib/server.dart +FILE: ../../../flutter/fml/ascii_trie.cc +FILE: ../../../flutter/fml/ascii_trie.h +FILE: ../../../flutter/fml/ascii_trie_unittests.cc FILE: ../../../flutter/fml/backtrace.cc FILE: ../../../flutter/fml/backtrace.h FILE: ../../../flutter/fml/backtrace_stub.cc diff --git a/common/settings.h b/common/settings.h index 1eddf4c16fead..c0914ae9970a7 100644 --- a/common/settings.h +++ b/common/settings.h @@ -91,6 +91,7 @@ struct Settings { bool enable_checked_mode = false; bool start_paused = false; bool trace_skia = false; + std::string trace_whitelist; bool trace_startup = false; bool trace_systrace = false; bool dump_skp_on_shader_compilation = false; diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 356b8e2baa79d..3a0b77515c4d8 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -10,6 +10,8 @@ import("//flutter/testing/testing.gni") source_set("fml") { sources = [ + "ascii_trie.cc", + "ascii_trie.h", "backtrace.h", "base32.cc", "base32.h", @@ -236,6 +238,7 @@ executable("fml_unittests") { testonly = true sources = [ + "ascii_trie_unittests.cc", "backtrace_unittests.cc", "base32_unittest.cc", "command_line_unittest.cc", diff --git a/fml/ascii_trie.cc b/fml/ascii_trie.cc new file mode 100644 index 0000000000000..57892591fca33 --- /dev/null +++ b/fml/ascii_trie.cc @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include "flutter/fml/ascii_trie.h" +#include "flutter/fml/logging.h" + +namespace fml { +typedef AsciiTrie::TrieNode TrieNode; +typedef AsciiTrie::TrieNodePtr TrieNodePtr; + +namespace { +void Add(TrieNodePtr* trie, const char* entry) { + int ch = entry[0]; + FML_DCHECK(ch < AsciiTrie::kMaxAsciiValue); + if (ch != 0) { + if (!*trie) { + *trie = std::make_unique(); + } + Add(&(*trie)->children[ch], entry + 1); + } +} + +TrieNodePtr MakeTrie(const std::vector& entries) { + TrieNodePtr result; + for (const std::string& entry : entries) { + Add(&result, entry.c_str()); + } + return result; +} +} // namespace + +void AsciiTrie::Fill(const std::vector& entries) { + node_ = MakeTrie(entries); +} + +bool AsciiTrie::Query(TrieNode* trie, const char* query) { + FML_DCHECK(trie); + const char* char_position = query; + TrieNode* trie_position = trie; + TrieNode* child; + int ch; + while ((ch = *char_position) && (child = trie_position->children[ch].get())) { + char_position++; + trie_position = child; + } + return !child && trie_position != trie; +} +} // namespace fml diff --git a/fml/ascii_trie.h b/fml/ascii_trie.h new file mode 100644 index 0000000000000..e6ff430a08e4f --- /dev/null +++ b/fml/ascii_trie.h @@ -0,0 +1,39 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FML_ASCIITRIE_H_ +#define FLUTTER_FML_ASCIITRIE_H_ + +#include +#include + +namespace fml { + +/// A trie for looking for ASCII prefixes. +class AsciiTrie { + public: + struct TrieNode; + typedef std::unique_ptr TrieNodePtr; + /// The max Ascii value. + static const int kMaxAsciiValue = 128; + + /// Clear and insert all the entries into the trie. + void Fill(const std::vector& entries); + + /// Returns true if \p argument is prefixed by the contents of the trie. + inline bool Query(const char* argument) { + return !node_ || Query(node_.get(), argument); + } + + struct TrieNode { + TrieNodePtr children[kMaxAsciiValue]; + }; + + private: + static bool Query(TrieNode* trie, const char* query); + TrieNodePtr node_; +}; +} // namespace fml + +#endif diff --git a/fml/ascii_trie_unittests.cc b/fml/ascii_trie_unittests.cc new file mode 100644 index 0000000000000..e11cb185d28bb --- /dev/null +++ b/fml/ascii_trie_unittests.cc @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/ascii_trie.h" +#include "gtest/gtest.h" + +using fml::AsciiTrie; + +TEST(AsciiTableTest, Simple) { + AsciiTrie trie; + auto entries = std::vector{"foo"}; + trie.Fill(entries); + ASSERT_TRUE(trie.Query("foobar")); + ASSERT_FALSE(trie.Query("google")); +} + +TEST(AsciiTableTest, ExactMatch) { + AsciiTrie trie; + auto entries = std::vector{"foo"}; + trie.Fill(entries); + ASSERT_TRUE(trie.Query("foo")); +} + +TEST(AsciiTableTest, Empty) { + AsciiTrie trie; + ASSERT_TRUE(trie.Query("foo")); +} + +TEST(AsciiTableTest, MultipleEntries) { + AsciiTrie trie; + auto entries = std::vector{"foo", "bar"}; + trie.Fill(entries); + ASSERT_TRUE(trie.Query("foozzz")); + ASSERT_TRUE(trie.Query("barzzz")); +} diff --git a/fml/trace_event.cc b/fml/trace_event.cc index 8698a2b0f203e..7e11b2c6f8e88 100644 --- a/fml/trace_event.cc +++ b/fml/trace_event.cc @@ -8,6 +8,7 @@ #include #include +#include "flutter/fml/ascii_trie.h" #include "flutter/fml/build_config.h" #include "flutter/fml/logging.h" @@ -22,6 +23,27 @@ namespace tracing { #if TIMELINE_ENABLED +namespace { +AsciiTrie gWhitelist; + +inline void FlutterTimelineEvent(const char* label, + int64_t timestamp0, + int64_t timestamp1_or_async_id, + Dart_Timeline_Event_Type type, + intptr_t argument_count, + const char** argument_names, + const char** argument_values) { + if (gWhitelist.Query(label)) { + Dart_TimelineEvent(label, timestamp0, timestamp1_or_async_id, type, + argument_count, argument_names, argument_values); + } +} +} // namespace + +void TraceSetWhitelist(const std::vector& whitelist) { + gWhitelist.Fill(whitelist); +} + size_t TraceNonce() { static std::atomic_size_t gLastItem; return ++gLastItem; @@ -42,7 +64,7 @@ void TraceTimelineEvent(TraceArg category_group, c_values[i] = values[i].c_str(); } - Dart_TimelineEvent( + FlutterTimelineEvent( name, // label Dart_TimelineGetMicros(), // timestamp0 identifier, // timestamp1_or_async_id @@ -54,13 +76,13 @@ void TraceTimelineEvent(TraceArg category_group, } void TraceEvent0(TraceArg category_group, TraceArg name) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - 0, // timestamp1_or_async_id - Dart_Timeline_Event_Begin, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Begin, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } @@ -70,13 +92,13 @@ void TraceEvent1(TraceArg category_group, TraceArg arg1_val) { const char* arg_names[] = {arg1_name}; const char* arg_values[] = {arg1_val}; - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - 0, // timestamp1_or_async_id - Dart_Timeline_Event_Begin, // event type - 1, // argument_count - arg_names, // argument_names - arg_values // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Begin, // event type + 1, // argument_count + arg_names, // argument_names + arg_values // argument_values ); } @@ -88,24 +110,24 @@ void TraceEvent2(TraceArg category_group, TraceArg arg2_val) { const char* arg_names[] = {arg1_name, arg2_name}; const char* arg_values[] = {arg1_val, arg2_val}; - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - 0, // timestamp1_or_async_id - Dart_Timeline_Event_Begin, // event type - 2, // argument_count - arg_names, // argument_names - arg_values // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Begin, // event type + 2, // argument_count + arg_names, // argument_names + arg_values // argument_values ); } void TraceEventEnd(TraceArg name) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - 0, // timestamp1_or_async_id - Dart_Timeline_Event_End, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_End, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } @@ -119,47 +141,47 @@ void TraceEventAsyncComplete(TraceArg category_group, std::swap(begin, end); } - Dart_TimelineEvent(name, // label - begin.ToEpochDelta().ToMicroseconds(), // timestamp0 - identifier, // timestamp1_or_async_id - Dart_Timeline_Event_Async_Begin, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + begin.ToEpochDelta().ToMicroseconds(), // timestamp0 + identifier, // timestamp1_or_async_id + Dart_Timeline_Event_Async_Begin, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); - Dart_TimelineEvent(name, // label - end.ToEpochDelta().ToMicroseconds(), // timestamp0 - identifier, // timestamp1_or_async_id - Dart_Timeline_Event_Async_End, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + end.ToEpochDelta().ToMicroseconds(), // timestamp0 + identifier, // timestamp1_or_async_id + Dart_Timeline_Event_Async_End, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } void TraceEventAsyncBegin0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - id, // timestamp1_or_async_id - Dart_Timeline_Event_Async_Begin, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_Begin, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } void TraceEventAsyncEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - id, // timestamp1_or_async_id - Dart_Timeline_Event_Async_End, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_End, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } @@ -170,13 +192,13 @@ void TraceEventAsyncBegin1(TraceArg category_group, TraceArg arg1_val) { const char* arg_names[] = {arg1_name}; const char* arg_values[] = {arg1_val}; - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - id, // timestamp1_or_async_id - Dart_Timeline_Event_Async_Begin, // event type - 1, // argument_count - arg_names, // argument_names - arg_values // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_Begin, // event type + 1, // argument_count + arg_names, // argument_names + arg_values // argument_values ); } @@ -187,66 +209,68 @@ void TraceEventAsyncEnd1(TraceArg category_group, TraceArg arg1_val) { const char* arg_names[] = {arg1_name}; const char* arg_values[] = {arg1_val}; - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - id, // timestamp1_or_async_id - Dart_Timeline_Event_Async_End, // event type - 1, // argument_count - arg_names, // argument_names - arg_values // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Async_End, // event type + 1, // argument_count + arg_names, // argument_names + arg_values // argument_values ); } void TraceEventInstant0(TraceArg category_group, TraceArg name) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - 0, // timestamp1_or_async_id - Dart_Timeline_Event_Instant, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + 0, // timestamp1_or_async_id + Dart_Timeline_Event_Instant, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } void TraceEventFlowBegin0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - id, // timestamp1_or_async_id - Dart_Timeline_Event_Flow_Begin, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Flow_Begin, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } void TraceEventFlowStep0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - id, // timestamp1_or_async_id - Dart_Timeline_Event_Flow_Step, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Flow_Step, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) { - Dart_TimelineEvent(name, // label - Dart_TimelineGetMicros(), // timestamp0 - id, // timestamp1_or_async_id - Dart_Timeline_Event_Flow_End, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values + FlutterTimelineEvent(name, // label + Dart_TimelineGetMicros(), // timestamp0 + id, // timestamp1_or_async_id + Dart_Timeline_Event_Flow_End, // event type + 0, // argument_count + nullptr, // argument_names + nullptr // argument_values ); } #else // TIMELINE_ENABLED +void TraceSetWhitelist(const std::vector& whitelist) {} + size_t TraceNonce() { return 0; } diff --git a/fml/trace_event.h b/fml/trace_event.h index a5b478ffc150e..a90aed2ff643e 100644 --- a/fml/trace_event.h +++ b/fml/trace_event.h @@ -112,6 +112,8 @@ namespace tracing { using TraceArg = const char*; using TraceIDArg = int64_t; +void TraceSetWhitelist(const std::vector& whitelist); + void TraceTimelineEvent(TraceArg category_group, TraceArg name, TraceIDArg id, diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 3a3f9cfa901ef..b115cb1bf60fa 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -180,6 +180,16 @@ static void RecordStartupTimestamp() { } } +static void Tokenize(const std::string& input, + std::vector* results, + char delimiter) { + std::istringstream ss(input); + std::string token; + while (std::getline(ss, token, delimiter)) { + results->push_back(token); + } +} + // Though there can be multiple shells, some settings apply to all components in // the process. These have to be setup before the shell or any of its // sub-components can be initialized. In a perfect world, this would be empty. @@ -205,6 +215,12 @@ static void PerformInitializationTasks(const Settings& settings) { InitSkiaEventTracer(settings.trace_skia); } + if (!settings.trace_whitelist.empty()) { + std::vector prefixes; + Tokenize(settings.trace_whitelist, &prefixes, ','); + fml::tracing::TraceSetWhitelist(prefixes); + } + if (!settings.skia_deterministic_rendering_on_cpu) { SkGraphics::Init(); } else { diff --git a/shell/common/switches.cc b/shell/common/switches.cc index a23e1156f5e5e..885763a107557 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -272,6 +272,9 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { settings.trace_skia = command_line.HasOption(FlagForSwitch(Switch::TraceSkia)); + command_line.GetOptionValue(FlagForSwitch(Switch::TraceWhitelist), + &settings.trace_whitelist); + settings.trace_systrace = command_line.HasOption(FlagForSwitch(Switch::TraceSystrace)); diff --git a/shell/common/switches.h b/shell/common/switches.h index c0faac25d50c8..e509b5cfd6160 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -129,6 +129,11 @@ DEF_SWITCH(TraceSkia, "Trace Skia calls. This is useful when debugging the GPU threed." "By default, Skia tracing is not enabled to reduce the number of " "traced events") +DEF_SWITCH( + TraceWhitelist, + "trace-whitelist", + "Filters out all trace events except those that are specified in this " + "comma separated list of whitelisted prefixes.") DEF_SWITCH(DumpSkpOnShaderCompilation, "dump-skp-on-shader-compilation", "Automatically dump the skp that triggers new shader compilations. " From 5a074882514f02c666e7fb3f20f983f776a84285 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 16 Mar 2020 11:41:44 -0700 Subject: [PATCH 018/386] [web] Add HtmlCodec progress callback (#17139) * Wire up htmlcodec chunkcallback for progress events * Add test for HtmlCodec --- .../lib/src/engine/html_image_codec.dart | 19 +++++++++++- lib/web_ui/lib/src/ui/painting.dart | 17 +++++----- .../engine/image/html_image_codec_test.dart | 29 ++++++++++++++++++ .../test/engine/image/sample_image1.png | Bin 0 -> 14746 bytes 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 lib/web_ui/test/engine/image/html_image_codec_test.dart create mode 100644 lib/web_ui/test/engine/image/sample_image1.png diff --git a/lib/web_ui/lib/src/engine/html_image_codec.dart b/lib/web_ui/lib/src/engine/html_image_codec.dart index ee48e62c541dd..28d3107fec24e 100644 --- a/lib/web_ui/lib/src/engine/html_image_codec.dart +++ b/lib/web_ui/lib/src/engine/html_image_codec.dart @@ -4,16 +4,21 @@ // @dart = 2.6 part of engine; + final bool _supportsDecode = js_util.getProperty( js_util.getProperty( js_util.getProperty(html.window, 'Image'), 'prototype'), 'decode') != null; +typedef WebOnlyImageCodecChunkCallback = void Function( + int cumulativeBytesLoaded, int expectedTotalBytes); + class HtmlCodec implements ui.Codec { final String src; + final WebOnlyImageCodecChunkCallback chunkCallback; - HtmlCodec(this.src); + HtmlCodec(this.src, {this.chunkCallback}); @override int get frameCount => 1; @@ -24,11 +29,20 @@ class HtmlCodec implements ui.Codec { @override Future getNextFrame() async { final Completer completer = Completer(); + // Currently there is no way to watch decode progress, so + // we add 0/100 , 100/100 progress callbacks to enable loading progress + // builders to create UI. + if (chunkCallback != null) { + chunkCallback(0, 100); + } if (_supportsDecode) { final html.ImageElement imgElement = html.ImageElement(); imgElement.src = src; js_util.setProperty(imgElement, 'decoding', 'async'); imgElement.decode().then((dynamic _) { + if (chunkCallback != null) { + chunkCallback(100, 100); + } final HtmlImage image = HtmlImage( imgElement, imgElement.naturalWidth, @@ -61,6 +75,9 @@ class HtmlCodec implements ui.Codec { completer.completeError(event); }); loadSubscription = imgElement.onLoad.listen((html.Event event) { + if (chunkCallback != null) { + chunkCallback(100, 100); + } loadSubscription.cancel(); errorSubscription.cancel(); final HtmlImage image = HtmlImage( diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index 3eced5b3c6ebe..9cef2faefb636 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -899,9 +899,8 @@ enum Clip { abstract class Paint { /// Constructs an empty [Paint] object with all fields initialized to /// their defaults. - factory Paint() => engine.experimentalUseSkia - ? engine.SkPaint() - : engine.SurfacePaint(); + factory Paint() => + engine.experimentalUseSkia ? engine.SkPaint() : engine.SurfacePaint(); /// Whether to dither the output when drawing images. /// @@ -1604,13 +1603,17 @@ String _instantiateImageCodec( return null; } -Future webOnlyInstantiateImageCodecFromUrl(Uri uri) { +Future webOnlyInstantiateImageCodecFromUrl(Uri uri, + {engine.WebOnlyImageCodecChunkCallback chunkCallback}) { return engine.futurize((engine.Callback callback) => - _instantiateImageCodecFromUrl(uri, callback)); + _instantiateImageCodecFromUrl(uri, chunkCallback, callback)); } -String _instantiateImageCodecFromUrl(Uri uri, engine.Callback callback) { - callback(engine.HtmlCodec(uri.toString())); +String _instantiateImageCodecFromUrl( + Uri uri, + engine.WebOnlyImageCodecChunkCallback chunkCallback, + engine.Callback callback) { + callback(engine.HtmlCodec(uri.toString(), chunkCallback: chunkCallback)); return null; } diff --git a/lib/web_ui/test/engine/image/html_image_codec_test.dart b/lib/web_ui/test/engine/image/html_image_codec_test.dart new file mode 100644 index 0000000000000..3c1f347162d1d --- /dev/null +++ b/lib/web_ui/test/engine/image/html_image_codec_test.dart @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +import 'package:test/test.dart'; +import 'package:ui/ui.dart' as ui; +import 'package:ui/src/engine.dart'; + +Future main() async { + await ui.webOnlyInitializeTestDomRenderer(); + group('HtmCodec', () { + test('loads sample image', () async { + final HtmlCodec codec = HtmlCodec('sample_image1.png'); + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + expect(frameInfo.image, isNotNull); + expect(frameInfo.image.width, 100); + }); + test('provides image loading progress', () async { + StringBuffer buffer = new StringBuffer(); + final HtmlCodec codec = HtmlCodec('sample_image1.png', + chunkCallback: (int loaded, int total) { + buffer.write('$loaded/$total,'); + }); + final ui.FrameInfo frameInfo = await codec.getNextFrame(); + expect(buffer.toString(), '0/100,100/100,'); + }); + }); +} diff --git a/lib/web_ui/test/engine/image/sample_image1.png b/lib/web_ui/test/engine/image/sample_image1.png new file mode 100644 index 0000000000000000000000000000000000000000..0d1393b8052134902c82d85a4c478db6a716684c GIT binary patch literal 14746 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 e(}mphinJVDosk2bwfW} Date: Mon, 16 Mar 2020 12:11:02 -0700 Subject: [PATCH 019/386] Two point conical gradient for web CanvasKit (#17146) --- lib/web_ui/lib/src/engine/shader.dart | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/shader.dart b/lib/web_ui/lib/src/engine/shader.dart index 128e3a01e063f..a64140bc83751 100644 --- a/lib/web_ui/lib/src/engine/shader.dart +++ b/lib/web_ui/lib/src/engine/shader.dart @@ -238,7 +238,25 @@ class GradientConical extends EngineGradient { @override js.JsObject createSkiaShader() { - throw UnimplementedError(); + assert(experimentalUseSkia); + + final js.JsArray jsColors = js.JsArray(); + jsColors.length = colors.length; + for (int i = 0; i < colors.length; i++) { + jsColors[i] = colors[i].value; + } + + return canvasKit.callMethod('MakeTwoPointConicalGradient', [ + makeSkPoint(focal), + focalRadius, + makeSkPoint(center), + radius, + jsColors, + makeSkiaColorStops(colorStops), + tileMode.index, + matrix4 != null ? makeSkMatrix(matrix4) : null, + 0, + ]); } } From 91faaab199ddbf61aeb73e4eae813cb7f94470af Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 19:01:02 -0400 Subject: [PATCH 020/386] Roll fuchsia/sdk/core/linux-amd64 from S9xSp... to YJI3-... (#17172) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c6b931097b884..09ca7c5b76fb6 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'S9xSpScwMVX6dLgXfPA_NAZ9zdl7hgqvXZgxBlr6tzkC' + 'version': 'YJI3-8r6MqhrSLIbGfx71hg-sUL8IUqlI3_IuAGmA64C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 591543c6618ef..38e9043569e9f 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: e2ec6f495e5d7854d864d8a261e829df +Signature: d3fea33ac3d1e3a5885406c606a2573b UNUSED LICENSES: From 6da4fc8fca2200d62deec2c6bae2a740d3141898 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 19:06:02 -0400 Subject: [PATCH 021/386] Roll src/third_party/skia 16701eecd595..04ef810839cb (10 commits) (#17174) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 09ca7c5b76fb6..729190b924c39 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '16701eecd5957e2d5eb0890eb2e9a72771d57941', + 'skia_revision': '04ef810839cbbe4bdd5419e2d09627bbdf94c678', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 4f880350a4930..86ca0596449c0 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f0e65dedaa9fb3e395875da83961406d +Signature: bef6212c4c1d95da94df8ab68518a7e0 UNUSED LICENSES: @@ -2441,7 +2441,6 @@ FILE: ../../../third_party/skia/src/gpu/GrDrawingManager.h FILE: ../../../third_party/skia/src/gpu/GrFragmentProcessor.cpp FILE: ../../../third_party/skia/src/gpu/GrGpuResourcePriv.h FILE: ../../../third_party/skia/src/gpu/GrManagedResource.h -FILE: ../../../third_party/skia/src/gpu/GrMesh.h FILE: ../../../third_party/skia/src/gpu/GrNonAtomicRef.h FILE: ../../../third_party/skia/src/gpu/GrOpFlushState.cpp FILE: ../../../third_party/skia/src/gpu/GrOpFlushState.h @@ -2454,6 +2453,7 @@ FILE: ../../../third_party/skia/src/gpu/GrRenderTargetPriv.h FILE: ../../../third_party/skia/src/gpu/GrResourceProvider.cpp FILE: ../../../third_party/skia/src/gpu/GrResourceProvider.h FILE: ../../../third_party/skia/src/gpu/GrSamplerState.h +FILE: ../../../third_party/skia/src/gpu/GrSimpleMesh.h FILE: ../../../third_party/skia/src/gpu/GrTessellator.cpp FILE: ../../../third_party/skia/src/gpu/GrTessellator.h FILE: ../../../third_party/skia/src/gpu/GrTestUtils.cpp From 2faadc71b60b490d1493e35cc874eaeef03861df Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 19:11:03 -0400 Subject: [PATCH 022/386] Roll src/third_party/dart d7a7854a5e98..810d85d4cf89 (21 commits) (#17176) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 729190b924c39..ae5aecbd0310b 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'd7a7854a5e98d0d447a24db1688ec69a1f0104f7', + 'dart_revision': '810d85d4cf8929dbe499f213a9c99a100f49fa1e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -62,7 +62,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.15.7', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.112', + 'dart_linter_tag': '0.1.113', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.5', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index df99ab055766f..b435bff7eadbd 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 9df43a93e8c8715e3373f456b338b59f +Signature: 086021ba2b77466be7dd13fb2490471a UNUSED LICENSES: From 1f67e7e015e354918100ab5f2599c3e98829584e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 16 Mar 2020 22:41:01 -0400 Subject: [PATCH 023/386] Roll src/third_party/skia 04ef810839cb..6b48e62ed204 (4 commits) (#17177) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ae5aecbd0310b..167db7adae61d 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '04ef810839cbbe4bdd5419e2d09627bbdf94c678', + 'skia_revision': '6b48e62ed204d5d84f3e6684959de096e33974f1', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 86ca0596449c0..fe2434b828c02 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: bef6212c4c1d95da94df8ab68518a7e0 +Signature: 84b54143dcfe5e9553f2d907b46f2a7e UNUSED LICENSES: From 083a7c2bcaaf8f2edd1994d2830e0b4a291e7342 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 17 Mar 2020 01:16:02 -0400 Subject: [PATCH 024/386] Roll src/third_party/dart 810d85d4cf89..db5dc11b9b27 (19 commits) (#17178) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 167db7adae61d..a20107b94a2a9 100644 --- a/DEPS +++ b/DEPS @@ -34,12 +34,12 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '810d85d4cf8929dbe499f213a9c99a100f49fa1e', + 'dart_revision': 'db5dc11b9b27d8fa0b4f9e2d1879829d058c80e8', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py 'dart_args_tag': '1.5.3', - 'dart_async_tag': '2.0.8', + 'dart_async_tag': '2.4.1', 'dart_bazel_worker_tag': 'v0.1.22', 'dart_boolean_selector_tag': '1.0.4', 'dart_boringssl_gen_rev': 'b9e27cff1ff0803e97ab1f88764a83be4aa94a6d', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index b435bff7eadbd..6845661f871cf 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 086021ba2b77466be7dd13fb2490471a +Signature: 56228b548fb4601b5ee5f50085425ab7 UNUSED LICENSES: From f00a13584ca384f3f7645fc53413966974fcce70 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 17 Mar 2020 02:26:02 -0400 Subject: [PATCH 025/386] Roll fuchsia/sdk/core/mac-amd64 from XRDIh... to AvvHp... (#17179) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index a20107b94a2a9..a6fa1f23d2a96 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'XRDIh5zK_woqoYL547JcCDq3LLX6COTvXOoTDv7tXn0C' + 'version': 'AvvHpw1Mm16GyITH-UL3wOmHL6B0yQNYPb9zmFuTvocC' } ], 'condition': 'host_os == "mac"', From 034f913b98ec7512e5f6865efbc0e774bdff34b6 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 17 Mar 2020 15:40:09 -0700 Subject: [PATCH 026/386] Teach frontend compiler to replace `toString` with `super.toString` for selected packages (#17068) Adds annotation `keepToString` to opt out. --- analysis_options.yaml | 10 +- ci/licenses_golden/licenses_flutter | 2 + flutter_frontend_server/lib/server.dart | 104 ++++++- .../test/fixtures/.gitignore | 1 + .../test/fixtures/.packages | 2 + .../test/fixtures/lib/main.dart | 26 ++ .../test/to_string_test.dart | 289 ++++++++++++++++++ lib/ui/annotations.dart | 46 +++ lib/ui/dart_ui.gni | 1 + lib/ui/ui.dart | 1 + lib/web_ui/lib/src/ui/annotations.dart | 46 +++ lib/web_ui/lib/ui.dart | 1 + testing/run_tests.py | 18 ++ 13 files changed, 539 insertions(+), 8 deletions(-) create mode 100644 flutter_frontend_server/test/fixtures/.gitignore create mode 100644 flutter_frontend_server/test/fixtures/.packages create mode 100644 flutter_frontend_server/test/fixtures/lib/main.dart create mode 100644 flutter_frontend_server/test/to_string_test.dart create mode 100644 lib/ui/annotations.dart create mode 100644 lib/web_ui/lib/src/ui/annotations.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 64c4409491bda..c125e1d52f749 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -10,9 +10,13 @@ # private fields, especially on the Window object): analyzer: - # this test pretends to be part of dart:ui and results in lots of false - # positives. - exclude: [ testing/dart/window_hooks_integration_test.dart ] + exclude: [ + # this test pretends to be part of dart:ui and results in lots of false + # positives. + testing/dart/window_hooks_integration_test.dart, + # Fixture depends on dart:ui and raises false positives. + flutter_frontend_server/test/fixtures/lib/main.dart + ] strong-mode: implicit-casts: false implicit-dynamic: false diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 0dac1de978ccc..21bb859d59146 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -269,6 +269,7 @@ FILE: ../../../flutter/lib/io/dart_io.cc FILE: ../../../flutter/lib/io/dart_io.h FILE: ../../../flutter/lib/snapshot/libraries.json FILE: ../../../flutter/lib/snapshot/snapshot.h +FILE: ../../../flutter/lib/ui/annotations.dart FILE: ../../../flutter/lib/ui/channel_buffers.dart FILE: ../../../flutter/lib/ui/compositing.dart FILE: ../../../flutter/lib/ui/compositing/scene.cc @@ -494,6 +495,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/util.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/validators.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/vector_math.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/window.dart +FILE: ../../../flutter/lib/web_ui/lib/src/ui/annotations.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/channel_buffers.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/compositing.dart diff --git a/flutter_frontend_server/lib/server.dart b/flutter_frontend_server/lib/server.dart index db095bcd05776..0c50557a71ecd 100644 --- a/flutter_frontend_server/lib/server.dart +++ b/flutter_frontend_server/lib/server.dart @@ -8,9 +8,6 @@ import 'dart:async'; import 'dart:io' hide FileSystemEntity; import 'package:args/args.dart'; -import 'package:path/path.dart' as path; - -import 'package:vm/incremental_compiler.dart'; import 'package:frontend_server/frontend_server.dart' as frontend show FrontendCompiler, @@ -19,6 +16,9 @@ import 'package:frontend_server/frontend_server.dart' as frontend argParser, usage, ProgramTransformer; +import 'package:kernel/ast.dart'; +import 'package:path/path.dart' as path; +import 'package:vm/incremental_compiler.dart'; /// Wrapper around [FrontendCompiler] that adds [widgetCreatorTracker] kernel /// transformation to the compilation. @@ -107,6 +107,13 @@ Future starter( frontend.ProgramTransformer transformer, }) async { ArgResults options; + frontend.argParser.addMultiOption( + 'delete-tostring-package-uri', + help: 'Replaces implementations of `toString` with `super.toString()` for ' + 'specified package', + valueHelp: 'dart:ui', + defaultsTo: const [], + ); try { options = frontend.argParser.parse(args); } catch (error) { @@ -115,6 +122,8 @@ Future starter( return 1; } + final Set deleteToStringPackageUris = (options['delete-tostring-package-uri'] as List).toSet(); + if (options['train'] as bool) { if (!options.rest.isNotEmpty) { throw Exception('Must specify input.dart'); @@ -137,7 +146,10 @@ Future starter( '--gen-bytecode', '--bytecode-options=source-positions,local-var-info,debugger-stops,instance-field-initializers,keep-unreachable-code,avoid-closure-call-instructions', ]); - compiler ??= _FlutterFrontendCompiler(output); + compiler ??= _FlutterFrontendCompiler( + output, + transformer: ToStringTransformer(null, deleteToStringPackageUris), + ); await compiler.compile(input, options); compiler.acceptLastDelta(); @@ -156,7 +168,7 @@ Future starter( } compiler ??= _FlutterFrontendCompiler(output, - transformer: transformer, + transformer: ToStringTransformer(transformer, deleteToStringPackageUris), useDebuggerModuleNames: options['debugger-module-names'] as bool, unsafePackageSerialization: options['unsafe-package-serialization'] as bool); @@ -169,3 +181,85 @@ Future starter( frontend.listenAndCompile(compiler, input ?? stdin, options, completer); return completer.future; } + +// Transformer/visitor for toString +// If we add any more of these, they really should go into a separate library. + +/// A [RecursiveVisitor] that replaces [Object.toString] overrides with +/// `super.toString()`. +class ToStringVisitor extends RecursiveVisitor { + /// The [packageUris] must not be null. + ToStringVisitor(this._packageUris) : assert(_packageUris != null); + + /// A set of package URIs to apply this transformer to, e.g. 'dart:ui' and + /// 'package:flutter/foundation.dart'. + final Set _packageUris; + + /// Turn 'dart:ui' into 'dart:ui', or + /// 'package:flutter/src/semantics_event.dart' into 'package:flutter'. + String _importUriToPackage(Uri importUri) => '${importUri.scheme}:${importUri.pathSegments.first}'; + + bool _isInTargetPackage(Procedure node) { + return _packageUris.contains(_importUriToPackage(node.enclosingLibrary.importUri)); + } + + bool _hasKeepAnnotation(Procedure node) { + for (ConstantExpression expression in node.annotations.whereType()) { + if (expression.constant is! InstanceConstant) { + continue; + } + final InstanceConstant constant = expression.constant as InstanceConstant; + if (constant.classNode.name == '_KeepToString' && constant.classNode.enclosingLibrary.importUri.toString() == 'dart:ui') { + return true; + } + } + return false; + } + + @override + void visitProcedure(Procedure node) { + if ( + node.name.name == 'toString' && + node.enclosingClass != null && + node.enclosingLibrary != null && + !node.isStatic && + !node.isAbstract && + _isInTargetPackage(node) && + !_hasKeepAnnotation(node) + ) { + node.function.body.replaceWith( + ReturnStatement( + SuperMethodInvocation( + node.name, + Arguments([]), + ), + ), + ); + } + } + + @override + void defaultMember(Member node) {} +} + +/// Replaces [Object.toString] overrides with calls to super for the specified +/// [packageUris]. +class ToStringTransformer extends frontend.ProgramTransformer { + /// The [packageUris] parameter must not be null, but may be empty. + ToStringTransformer(this._child, this._packageUris) : assert(_packageUris != null); + + final frontend.ProgramTransformer _child; + + /// A set of package URIs to apply this transformer to, e.g. 'dart:ui' and + /// 'package:flutter/foundation.dart'. + final Set _packageUris; + + @override + void transform(Component component) { + assert(_child is! ToStringTransformer); + if (_packageUris.isNotEmpty) { + component.visitChildren(ToStringVisitor(_packageUris)); + } + _child?.transform(component); + } +} diff --git a/flutter_frontend_server/test/fixtures/.gitignore b/flutter_frontend_server/test/fixtures/.gitignore new file mode 100644 index 0000000000000..160ff8892218b --- /dev/null +++ b/flutter_frontend_server/test/fixtures/.gitignore @@ -0,0 +1 @@ +!.packages diff --git a/flutter_frontend_server/test/fixtures/.packages b/flutter_frontend_server/test/fixtures/.packages new file mode 100644 index 0000000000000..6164a98c24f5a --- /dev/null +++ b/flutter_frontend_server/test/fixtures/.packages @@ -0,0 +1,2 @@ +# Generated by pub on 2020-01-15 10:08:29.776333. +flutter_frontend_fixtures:lib/ diff --git a/flutter_frontend_server/test/fixtures/lib/main.dart b/flutter_frontend_server/test/fixtures/lib/main.dart new file mode 100644 index 0000000000000..bfa11f7d1bc5f --- /dev/null +++ b/flutter_frontend_server/test/fixtures/lib/main.dart @@ -0,0 +1,26 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:convert'; +import 'dart:ui'; + +void main() { + final Paint paint = Paint()..color = Color(0xFFFFFFFF); + print(jsonEncode({ + 'Paint.toString': paint.toString(), + 'Foo.toString': Foo().toString(), + 'Keep.toString': Keep().toString(), + })); +} + +class Foo { + @override + String toString() => 'I am a Foo'; +} + +class Keep { + @keepToString + @override + String toString() => 'I am a Keep'; +} diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart new file mode 100644 index 0000000000000..ac70b23697ad3 --- /dev/null +++ b/flutter_frontend_server/test/to_string_test.dart @@ -0,0 +1,289 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter_frontend_server/server.dart'; +import 'package:frontend_server/frontend_server.dart' as frontend show ProgramTransformer; +import 'package:kernel/kernel.dart'; +import 'package:mockito/mockito.dart'; +import 'package:path/path.dart' as path; + +import 'package:test/test.dart'; + +void main(List args) async { + if (args.length != 2) { + stderr.writeln('The first argument must be the path to the forntend server dill.'); + stderr.writeln('The second argument must be the path to the flutter_patched_sdk'); + exit(-1); + } + + const Set uiAndFlutter = { + 'dart:ui', + 'package:flutter', + }; + + test('No packages', () { + final ToStringTransformer transformer = ToStringTransformer(null, {}); + + final MockComponent component = MockComponent(); + transformer.transform(component); + verifyNever(component.visitChildren(any)); + }); + + test('dart:ui package', () { + final ToStringTransformer transformer = ToStringTransformer(null, uiAndFlutter); + + final MockComponent component = MockComponent(); + transformer.transform(component); + verify(component.visitChildren(any)).called(1); + }); + + test('Child transformer', () { + final MockTransformer childTransformer = MockTransformer(); + final ToStringTransformer transformer = ToStringTransformer(childTransformer, {}); + + final MockComponent component = MockComponent(); + transformer.transform(component); + verifyNever(component.visitChildren(any)); + verify(childTransformer.transform(component)).called(1); + }); + + test('ToStringVisitor ignores non-toString procedures', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + when(procedure.name).thenReturn(Name('main')); + when(procedure.annotations).thenReturn(const []); + + visitor.visitProcedure(procedure); + verifyNever(procedure.enclosingLibrary); + }); + + test('ToStringVisitor ignores top level toString', () { + // i.e. a `toString` method specified at the top of a library, like: + // + // void main() {} + // String toString() => 'why?'; + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(Name('toString')); + when(procedure.annotations).thenReturn(const []); + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(null); + when(procedure.isAbstract).thenReturn(false); + when(procedure.isStatic).thenReturn(false); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + verifyNever(statement.replaceWith(any)); + }); + + test('ToStringVisitor ignores abstract toString', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(Name('toString')); + when(procedure.annotations).thenReturn(const []); + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(Class()); + when(procedure.isAbstract).thenReturn(true); + when(procedure.isStatic).thenReturn(false); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + verifyNever(statement.replaceWith(any)); + }); + + test('ToStringVisitor ignores static toString', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(Name('toString')); + when(procedure.annotations).thenReturn(const []); + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(Class()); + when(procedure.isAbstract).thenReturn(false); + when(procedure.isStatic).thenReturn(true); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + verifyNever(statement.replaceWith(any)); + }); + + test('ToStringVisitor ignores non-specified libraries', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(Name('toString')); + when(procedure.annotations).thenReturn(const []); + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(Class()); + when(procedure.isAbstract).thenReturn(false); + when(procedure.isStatic).thenReturn(false); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + verifyNever(statement.replaceWith(any)); + }); + + test('ToStringVisitor ignores @keepToString', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('dart:ui')); + final Name name = Name('toString'); + final Class annotation = Class(name: '_KeepToString')..parent = Library(Uri.parse('dart:ui')); + + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(name); + when(procedure.annotations).thenReturn([ + ConstantExpression( + InstanceConstant( + Reference()..node = annotation, + [], + {}, + ), + ), + ]); + + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(Class()); + when(procedure.isAbstract).thenReturn(false); + when(procedure.isStatic).thenReturn(false); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + verifyNever(statement.replaceWith(any)); + }); + + void _validateReplacement(MockStatement body) { + final ReturnStatement replacement = verify(body.replaceWith(captureAny)).captured.single as ReturnStatement; + expect(replacement.expression, isA()); + final SuperMethodInvocation superMethodInvocation = replacement.expression as SuperMethodInvocation; + expect(superMethodInvocation.name.name, 'toString'); + } + + test('ToStringVisitor replaces toString in specified libraries (dart:ui)', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('dart:ui')); + final Name name = Name('toString'); + + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(name); + when(procedure.annotations).thenReturn(const []); + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(Class()); + when(procedure.isAbstract).thenReturn(false); + when(procedure.isStatic).thenReturn(false); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + _validateReplacement(statement); + }); + + test('ToStringVisitor replaces toString in specified libraries (package:flutter)', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('package:flutter/src/foundation.dart')); + final Name name = Name('toString'); + + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(name); + when(procedure.annotations).thenReturn(const []); + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(Class()); + when(procedure.isAbstract).thenReturn(false); + when(procedure.isStatic).thenReturn(false); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + _validateReplacement(statement); + }); + + group('Integration tests', () { + final String dart = Platform.resolvedExecutable; + final String frontendServer = args[0]; + final String sdkRoot = args[1]; + final String basePath = path.canonicalize(path.join(path.dirname(Platform.script.path), '..')); + final String fixtures = path.join(basePath, 'test', 'fixtures'); + final String mainDart = path.join(fixtures, 'lib', 'main.dart'); + final String dotPackages = path.join(fixtures, '.packages'); + final String regularDill = path.join(fixtures, 'toString.dill'); + final String transformedDill = path.join(fixtures, 'toStringTransformed.dill'); + + + void _checkProcessResult(ProcessResult result) { + if (result.exitCode != 0) { + stdout.writeln(result.stdout); + stderr.writeln(result.stderr); + } + expect(result.exitCode, 0); + } + + test('Without flag', () async { + _checkProcessResult(Process.runSync(dart, [ + frontendServer, + '--sdk-root=$sdkRoot', + '--target=flutter', + '--packages=$dotPackages', + '--output-dill=$regularDill', + mainDart, + ])); + final ProcessResult runResult = Process.runSync(dart, [regularDill]); + _checkProcessResult(runResult); + expect( + runResult.stdout.trim(), + '{"Paint.toString":"Paint(Color(0xffffffff))",' + '"Foo.toString":"I am a Foo",' + '"Keep.toString":"I am a Keep"}', + ); + }); + + test('With flag', () async { + _checkProcessResult(Process.runSync(dart, [ + frontendServer, + '--sdk-root=$sdkRoot', + '--target=flutter', + '--packages=$dotPackages', + '--output-dill=$transformedDill', + '--delete-tostring-package-uri', 'dart:ui', + '--delete-tostring-package-uri', 'package:flutter_frontend_fixtures', + mainDart, + ])); + final ProcessResult runResult = Process.runSync(dart, [transformedDill]); + _checkProcessResult(runResult); + expect( + runResult.stdout.trim(), + '{"Paint.toString":"Instance of \'Paint\'",' + '"Foo.toString":"Instance of \'Foo\'",' + '"Keep.toString":"I am a Keep"}', + ); + }); + }); +} + +class MockComponent extends Mock implements Component {} +class MockTransformer extends Mock implements frontend.ProgramTransformer {} +class MockProcedure extends Mock implements Procedure {} +class MockFunctionNode extends Mock implements FunctionNode {} +class MockStatement extends Mock implements Statement {} diff --git a/lib/ui/annotations.dart b/lib/ui/annotations.dart new file mode 100644 index 0000000000000..825c8637b22e8 --- /dev/null +++ b/lib/ui/annotations.dart @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. + +// @dart = 2.6 +part of dart.ui; + +// TODO(dnfield): Update this if/when we default this to on in the tool, +// see: https://github.com/flutter/flutter/issues/52759 +/// Annotation used by Flutter's Dart compiler to indicate that an +/// [Object.toString] override should not be replaced with a supercall. +/// +/// Since `dart:ui` and `package:flutter` override `toString` purely for +/// debugging purposes, the frontend compiler is instructed to replace all +/// `toString` bodies with `return super.toString()` during compilation. This +/// significantly reduces release code size, and would make it impossible to +/// implement a meaningful override of `toString` for release mode without +/// disabling the feature and losing the size savings. If a package uses this +/// feature and has some unavoidable need to keep the `toString` implementation +/// for a specific class, applying this annotation will direct the compiler +/// to leave the method body as-is. +/// +/// For example, in the following class the `toString` method will remain as +/// `return _buffer.toString();`, even if the `--delete-tostring-package-uri` +/// option would otherwise apply and replace it with `return super.toString()`. +/// +/// ```dart +/// class MyStringBuffer { +/// StringBuffer _buffer = StringBuffer(); +/// +/// // ... +/// +/// @keepToString +/// @override +/// String toString() { +/// return _buffer.toString(); +/// } +/// } +/// ``` +const _KeepToString keepToString = _KeepToString(); + +class _KeepToString { + const _KeepToString(); +} diff --git a/lib/ui/dart_ui.gni b/lib/ui/dart_ui.gni index 3eef51f003e4a..28cb82e9cd125 100644 --- a/lib/ui/dart_ui.gni +++ b/lib/ui/dart_ui.gni @@ -3,6 +3,7 @@ # found in the LICENSE file. dart_ui_files = [ + "//flutter/lib/ui/annotations.dart", "//flutter/lib/ui/channel_buffers.dart", "//flutter/lib/ui/compositing.dart", "//flutter/lib/ui/geometry.dart", diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index 2f8692c803d15..2d46db0404f6f 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -23,6 +23,7 @@ import 'dart:math' as math; import 'dart:nativewrappers'; import 'dart:typed_data'; +part 'annotations.dart'; part 'channel_buffers.dart'; part 'compositing.dart'; part 'geometry.dart'; diff --git a/lib/web_ui/lib/src/ui/annotations.dart b/lib/web_ui/lib/src/ui/annotations.dart new file mode 100644 index 0000000000000..10b9ef2694b1f --- /dev/null +++ b/lib/web_ui/lib/src/ui/annotations.dart @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(dnfield): Remove unused_import ignores when https://github.com/dart-lang/sdk/issues/35164 is resolved. + +// @dart = 2.6 +part of ui; + +// TODO(dnfield): Update this if/when we default this to on in the tool, +// see: https://github.com/flutter/flutter/issues/52759 +/// Annotation used by Flutter's Dart compiler to indicate that an +/// [Object.toString] override should not be replaced with a supercall. +/// +/// Since `dart:ui` and `package:flutter` override `toString` purely for +/// debugging purposes, the frontend compiler is instructed to replace all +/// `toString` bodies with `return super.toString()` during compilation. This +/// significantly reduces release code size, and would make it impossible to +/// implement a meaningful override of `toString` for release mode without +/// disabling the feature and losing the size savings. If a package uses this +/// feature and has some unavoidable need to keep the `toString` implementation +/// for a specific class, applying this annotation will direct the compiler +/// to leave the method body as-is. +/// +/// For example, in the following class the `toString` method will remain as +/// `return _buffer.toString();`, even if the `--delete-tostring-package-uri` +/// option would otherwise apply and replace it with `return super.toString()`. +/// +/// ```dart +/// class MyStringBuffer { +/// StringBuffer _buffer = StringBuffer(); +/// +/// // ... +/// +/// @keepToString +/// @override +/// String toString() { +/// return _buffer.toString(); +/// } +/// } +/// ``` +const _KeepToString keepToString = _KeepToString(); + +class _KeepToString { + const _KeepToString(); +} diff --git a/lib/web_ui/lib/ui.dart b/lib/web_ui/lib/ui.dart index 4acb5e246bfd8..8565c42d034e3 100644 --- a/lib/web_ui/lib/ui.dart +++ b/lib/web_ui/lib/ui.dart @@ -24,6 +24,7 @@ export 'src/engine.dart' webOnlySetPluginHandler, webOnlyInitializeEngine; +part 'src/ui/annotations.dart'; part 'src/ui/canvas.dart'; part 'src/ui/channel_buffers.dart'; part 'src/ui/compositing.dart'; diff --git a/testing/run_tests.py b/testing/run_tests.py index 90c3a6067b26f..658a982ff808a 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -330,6 +330,23 @@ def RunDartTests(build_dir, filter, verbose_dart_snapshot): RunDartTest(build_dir, dart_test_file, verbose_dart_snapshot, True) RunDartTest(build_dir, dart_test_file, verbose_dart_snapshot, False) + +def RunFrontEndServerTests(build_dir): + test_dir = os.path.join(buildroot_dir, 'flutter', 'flutter_frontend_server') + dart_tests = glob.glob('%s/test/*_test.dart' % test_dir) + for dart_test_file in dart_tests: + opts = [ + dart_test_file, + os.path.join(build_dir, 'gen', 'frontend_server.dart.snapshot'), + os.path.join(build_dir, 'flutter_patched_sdk')] + RunEngineExecutable( + build_dir, + os.path.join('dart-sdk', 'bin', 'dart'), + None, + flags=opts, + cwd=test_dir) + + def RunConstFinderTests(build_dir): test_dir = os.path.join(buildroot_dir, 'flutter', 'tools', 'const_finder', 'test') opts = [ @@ -376,6 +393,7 @@ def main(): dart_filter = args.dart_filter.split(',') if args.dart_filter else None RunDartTests(build_dir, dart_filter, args.verbose_dart_snapshot) RunConstFinderTests(build_dir) + RunFrontEndServerTests(build_dir) if 'java' in types: assert not IsWindows(), "Android engine files can't be compiled on Windows." From 575ba8b5227eb0c22f5dc5abf87db0018fb4d4d0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 17 Mar 2020 19:26:02 -0400 Subject: [PATCH 027/386] Roll src/third_party/skia 6b48e62ed204..a17558c9f702 (3 commits) (#17180) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a6fa1f23d2a96..8fcd0f1b9cde1 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6b48e62ed204d5d84f3e6684959de096e33974f1', + 'skia_revision': 'a17558c9f70262eace249a4546aff2375d738050', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index fe2434b828c02..aff2b85ed4965 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 84b54143dcfe5e9553f2d907b46f2a7e +Signature: 1d2146938d498ef1d575dbcb48b3a6c4 UNUSED LICENSES: From 8d0076cb2ad551222f320e8fc0be1ae4b26b6251 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 17 Mar 2020 19:31:04 -0400 Subject: [PATCH 028/386] Roll fuchsia/sdk/core/linux-amd64 from YJI3-... to p9B8q... (#17183) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 8fcd0f1b9cde1..cdd0372ac770f 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'YJI3-8r6MqhrSLIbGfx71hg-sUL8IUqlI3_IuAGmA64C' + 'version': 'p9B8qVYSEoGGPvRk2-f7kF3QQ9J8WXc3O9y3_BF_dmwC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 38e9043569e9f..43cd98ee38927 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: d3fea33ac3d1e3a5885406c606a2573b +Signature: 02005f102f898d40cb6578d777a11227 UNUSED LICENSES: From c85f3f0662a1a4929f4b092f3916c72832030eed Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 17 Mar 2020 19:36:02 -0400 Subject: [PATCH 029/386] Roll src/third_party/dart db5dc11b9b27..9dfd0127daca (20 commits) (#17187) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index cdd0372ac770f..d92c34fcd3c4b 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'db5dc11b9b27d8fa0b4f9e2d1879829d058c80e8', + 'dart_revision': '9dfd0127daca6e22009235e2a2e5bc9168674884', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -78,7 +78,7 @@ vars = { 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', 'dart_pub_rev': '429a06039d185149f387a65e3503b0693ce6d24e', - 'dart_pub_semver_tag': '1.4.2', + 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', 'dart_root_certificates_rev': '16ef64be64c7dfdff2b9f4b910726e635ccc519e', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 6845661f871cf..b7f65004e4d6c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 56228b548fb4601b5ee5f50085425ab7 +Signature: e37c5e14e602cb25f0926be1d16143cb UNUSED LICENSES: @@ -7778,6 +7778,11 @@ FILE: ../../../third_party/dart/runtime/vm/dispatch_table.h FILE: ../../../third_party/dart/runtime/vm/field_table.cc FILE: ../../../third_party/dart/runtime/vm/field_table.h FILE: ../../../third_party/dart/runtime/vm/timeline_macos.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test.h +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test_arm.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test_arm64.cc +FILE: ../../../third_party/dart/runtime/vm/type_testing_stubs_test_x64.cc FILE: ../../../third_party/dart/runtime/vm/visitor.cc FILE: ../../../third_party/dart/samples/ffi/async/async_test.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_async_callback.dart From 710f738dd6d484c89cdee297a5fc20e3c62aae00 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 17 Mar 2020 19:04:18 -0700 Subject: [PATCH 030/386] Allow relative resource paths in GLFW embedding (#16944) Currently every Linux runner has this code to allow relative resource paths; this moves it into the framework so that any embedder can get this behavior without that code needing to be in the template. Rolls buildroot to pick up std::filesystem support in our libc++ --- BUILD.gn | 1 + DEPS | 2 +- ci/licenses_golden/licenses_flutter | 3 ++ shell/platform/common/cpp/BUILD.gn | 41 +++++++++++++++++++ shell/platform/common/cpp/path_utils.cc | 37 +++++++++++++++++ shell/platform/common/cpp/path_utils.h | 18 ++++++++ .../common/cpp/path_utils_unittests.cc | 24 +++++++++++ shell/platform/glfw/flutter_glfw.cc | 25 ++++++++++- shell/platform/glfw/public/flutter_glfw.h | 4 ++ testing/run_tests.py | 2 + 10 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 shell/platform/common/cpp/path_utils.cc create mode 100644 shell/platform/common/cpp/path_utils.h create mode 100644 shell/platform/common/cpp/path_utils_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index 25006866ca896..c62b1e9610067 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -75,6 +75,7 @@ group("flutter") { "//flutter/lib/ui:ui_unittests", "//flutter/runtime:runtime_unittests", "//flutter/shell/common:shell_unittests", + "//flutter/shell/platform/common/cpp:common_cpp_core_unittests", "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper_unittests", "//flutter/shell/platform/embedder:embedder_unittests", "//flutter/shell/platform/glfw/client_wrapper:client_wrapper_glfw_unittests", diff --git a/DEPS b/DEPS index d92c34fcd3c4b..dda1cc60fc12b 100644 --- a/DEPS +++ b/DEPS @@ -137,7 +137,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'd14f3c708fb132381f7053b4ee9e628be915ed96', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '4489a78bf94bb9d8ea6a127106f85b7710d57f70', # Fuchsia compatibility # diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 21bb859d59146..7a06e1efa04bd 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -798,6 +798,9 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_message FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.cc FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.h +FILE: ../../../flutter/shell/platform/common/cpp/path_utils.cc +FILE: ../../../flutter/shell/platform/common/cpp/path_utils.h +FILE: ../../../flutter/shell/platform/common/cpp/path_utils_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/public/flutter_export.h FILE: ../../../flutter/shell/platform/common/cpp/public/flutter_messenger.h FILE: ../../../flutter/shell/platform/common/cpp/public/flutter_plugin_registrar.h diff --git a/shell/platform/common/cpp/BUILD.gn b/shell/platform/common/cpp/BUILD.gn index b4d5b0a5bf88b..00db206ed2a70 100644 --- a/shell/platform/common/cpp/BUILD.gn +++ b/shell/platform/common/cpp/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//flutter/testing/testing.gni") + config("desktop_library_implementation") { defines = [ "FLUTTER_DESKTOP_LIBRARY" ] } @@ -49,6 +51,10 @@ source_set("common_cpp") { "//flutter/shell/platform/embedder:embedder_with_symbol_prefix", ] + public_deps = [ + ":common_cpp_core", + ] + # TODO: Remove once text input model refactor lands, at which point this code # won't have a JSON dependency. defines = [ "USE_RAPID_JSON" ] @@ -60,6 +66,41 @@ source_set("common_cpp") { } } +# The portion of common_cpp that has no dependencies on the public/ +# headers. This division should be revisited once the Linux GTK +# embedding is futher along and it's clearer how much, if any, shared +# API surface there will be. +source_set("common_cpp_core") { + public = [ + "path_utils.h", + ] + + sources = [ + "path_utils.cc", + ] +} + +test_fixtures("common_cpp_core_fixtures") { + fixtures = [] +} + +executable("common_cpp_core_unittests") { + testonly = true + + sources = [ + "path_utils_unittests.cc", + ] + + deps = [ + ":common_cpp_core", + ":common_cpp_core_fixtures", + "//flutter/testing", + "//third_party/dart/runtime:libdart_jit", + ] + + public_configs = [ "//flutter:config" ] +} + copy("publish_headers") { sources = _public_headers outputs = [ diff --git a/shell/platform/common/cpp/path_utils.cc b/shell/platform/common/cpp/path_utils.cc new file mode 100644 index 0000000000000..6da31077a761e --- /dev/null +++ b/shell/platform/common/cpp/path_utils.cc @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/common/cpp/path_utils.h" + +#if defined(_WIN32) +#include +#elif defined(__linux__) +#include +#include +#endif + +namespace flutter { + +std::filesystem::path GetExecutableDirectory() { +#if defined(_WIN32) + wchar_t buffer[MAX_PATH]; + if (GetModuleFileName(nullptr, buffer, MAX_PATH) == 0) { + return std::filesystem::path(); + } + std::filesystem::path executable_path(buffer); + return executable_path.remove_filename(); +#elif defined(__linux__) + char buffer[PATH_MAX + 1]; + ssize_t length = readlink("/proc/self/exe", buffer, sizeof(buffer)); + if (length > PATH_MAX) { + return std::filesystem::path(); + } + std::filesystem::path executable_path(std::string(buffer, length)); + return executable_path.remove_filename(); +#else + return std::filesystem::path(); +#endif +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/path_utils.h b/shell/platform/common/cpp/path_utils.h new file mode 100644 index 0000000000000..62cd3fd840a9e --- /dev/null +++ b/shell/platform/common/cpp/path_utils.h @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_GLFW_PATH_UTILS_H_ +#define FLUTTER_SHELL_PLATFORM_GLFW_PATH_UTILS_H_ + +#include + +namespace flutter { + +// Returns the path of the directory containing this executable, or an empty +// path if the directory cannot be found. +std::filesystem::path GetExecutableDirectory(); + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_GLFW_PATH_UTILS_H_ diff --git a/shell/platform/common/cpp/path_utils_unittests.cc b/shell/platform/common/cpp/path_utils_unittests.cc new file mode 100644 index 0000000000000..2044d34662bc4 --- /dev/null +++ b/shell/platform/common/cpp/path_utils_unittests.cc @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/common/cpp/path_utils.h" + +#include "gtest/gtest.h" + +namespace flutter { + +// Tests that GetExecutableDirectory returns a valid, absolute path. +TEST(PathUtilsTest, ExecutableDirector) { + std::filesystem::path exe_directory = GetExecutableDirectory(); +#if defined(__linux__) || defined(_WIN32) + EXPECT_EQ(exe_directory.empty(), false); + EXPECT_EQ(exe_directory.is_absolute(), true); +#else + // On platforms where it's not implemented, it should indicate that + // by returning an empty path. + EXPECT_EQ(exe_directory.empty(), true); +#endif +} + +} // namespace flutter diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index da1176f4f0f5b..f79c360fa88d1 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -10,10 +10,12 @@ #include #include #include +#include #include #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h" #include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h" +#include "flutter/shell/platform/common/cpp/path_utils.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/glfw/glfw_event_loop.h" #include "flutter/shell/platform/glfw/key_event_handler.h" @@ -507,6 +509,25 @@ static FLUTTER_API_SYMBOL(FlutterEngine) &engine_properties.switches[engine_properties.switches_count]); } + std::filesystem::path assets_path = + std::filesystem::u8path(engine_properties.assets_path); + std::filesystem::path icu_path = + std::filesystem::u8path(engine_properties.icu_data_path); + if (assets_path.is_relative() || icu_path.is_relative()) { + // Treat relative paths as relative to the directory of this executable. + std::filesystem::path executable_location = + flutter::GetExecutableDirectory(); + if (executable_location.empty()) { + std::cerr << "Unable to find executable location to resolve paths." + << std::endl; + return nullptr; + } + assets_path = std::filesystem::path(executable_location) / assets_path; + icu_path = std::filesystem::path(executable_location) / icu_path; + } + std::string assets_path_string = assets_path.u8string(); + std::string icu_path_string = assets_path.u8string(); + FlutterRendererConfig config = {}; if (window == nullptr) { config.type = kOpenGL; @@ -528,8 +549,8 @@ static FLUTTER_API_SYMBOL(FlutterEngine) } FlutterProjectArgs args = {}; args.struct_size = sizeof(FlutterProjectArgs); - args.assets_path = engine_properties.assets_path; - args.icu_data_path = engine_properties.icu_data_path; + args.assets_path = assets_path_string.c_str(); + args.icu_data_path = icu_path_string.c_str(); args.command_line_argc = static_cast(argv.size()); args.command_line_argv = &argv[0]; args.platform_message_callback = GLFWOnFlutterPlatformMessage; diff --git a/shell/platform/glfw/public/flutter_glfw.h b/shell/platform/glfw/public/flutter_glfw.h index 610c63ff7948a..f9921bf36ca33 100644 --- a/shell/platform/glfw/public/flutter_glfw.h +++ b/shell/platform/glfw/public/flutter_glfw.h @@ -38,8 +38,12 @@ typedef struct { // Properties for configuring a Flutter engine instance. typedef struct { // The path to the flutter_assets folder for the application to be run. + // This can either be an absolute path, or on Windows or Linux, a path + // relative to the directory containing the executable. const char* assets_path; // The path to the icudtl.dat file for the version of Flutter you are using. + // This can either be an absolute path, or on Windows or Linux, a path + // relative to the directory containing the executable. const char* icu_data_path; // The switches to pass to the Flutter engine. // diff --git a/testing/run_tests.py b/testing/run_tests.py index 658a982ff808a..083117458d448 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -108,6 +108,8 @@ def RunCCTests(build_dir, filter): RunEngineExecutable(build_dir, 'client_wrapper_glfw_unittests', filter, shuffle_flags) + RunEngineExecutable(build_dir, 'common_cpp_core_unittests', filter, shuffle_flags) + RunEngineExecutable(build_dir, 'client_wrapper_unittests', filter, shuffle_flags) # https://github.com/flutter/flutter/issues/36294 From 3a2cc9553346121760f6d7478ee17ca59f0aec94 Mon Sep 17 00:00:00 2001 From: Wu Zhong Date: Wed, 18 Mar 2020 12:38:53 +0800 Subject: [PATCH 031/386] Fixes comments typo in FlutterPlatformViews (#17094) --- .../darwin/ios/framework/Headers/FlutterPlatformViews.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h index 72086dec13a83..ba22e62b33394 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h @@ -31,7 +31,7 @@ FLUTTER_EXPORT * * The implementation of this method should create a new `UIView` and return it. * - * @param frame The rectangle for the newly created `UIView` measued in points. + * @param frame The rectangle for the newly created `UIView` measured in points. * @param viewId A unique identifier for this `UIView`. * @param args Parameters for creating the `UIView` sent from the Dart side of the Flutter app. * If `createArgsCodec` is not implemented, or if no creation arguments were sent from the Dart From 4196207ee70e1f35557441ba65fa11d14e3a47f0 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 17 Mar 2020 22:31:01 -0700 Subject: [PATCH 032/386] Reland "Add support for the Metal backend on all iOS builds." (#17191) --- tools/gn | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tools/gn b/tools/gn index b7cb71763da95..3af8a805a33ef 100755 --- a/tools/gn +++ b/tools/gn @@ -44,9 +44,6 @@ def get_out_dir(args): if args.enable_vulkan: target_dir.append('vulkan') - if args.enable_metal and args.target_os == 'ios': - target_dir.append('metal') - return os.path.join(args.out_dir, 'out', '_'.join(target_dir)) def to_command_line(gn_args): @@ -219,17 +216,17 @@ def to_gn_args(args): gn_args['use_goma'] = False gn_args['goma_dir'] = None - if args.enable_metal: - gn_args['skia_use_metal'] = True - gn_args['shell_enable_metal'] = True - gn_args['allow_deprecated_api_calls'] = True - if args.enable_vulkan: # Enable vulkan in the Flutter shell. gn_args['shell_enable_vulkan'] = True # Configure Skia for Vulkan support. gn_args['skia_use_vulkan'] = True + # Enable Metal on non-simulator iOS builds. + if args.target_os == 'ios': + gn_args['skia_use_metal'] = not args.simulator + gn_args['shell_enable_metal'] = not args.simulator + # The buildroot currently isn't set up to support Vulkan in the # Windows ANGLE build, so disable it regardless of enable_vulkan's value. if sys.platform.startswith(('cygwin', 'win')): From 758de2d05d1db9811bbf404ea40ec8c53aac0755 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Wed, 18 Mar 2020 11:29:47 -0700 Subject: [PATCH 033/386] Revert "Add support for the Metal backend on all iOS builds." (#17197) This reverts commit 4196207ee70e1f35557441ba65fa11d14e3a47f0. --- tools/gn | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/gn b/tools/gn index 3af8a805a33ef..b7cb71763da95 100755 --- a/tools/gn +++ b/tools/gn @@ -44,6 +44,9 @@ def get_out_dir(args): if args.enable_vulkan: target_dir.append('vulkan') + if args.enable_metal and args.target_os == 'ios': + target_dir.append('metal') + return os.path.join(args.out_dir, 'out', '_'.join(target_dir)) def to_command_line(gn_args): @@ -216,17 +219,17 @@ def to_gn_args(args): gn_args['use_goma'] = False gn_args['goma_dir'] = None + if args.enable_metal: + gn_args['skia_use_metal'] = True + gn_args['shell_enable_metal'] = True + gn_args['allow_deprecated_api_calls'] = True + if args.enable_vulkan: # Enable vulkan in the Flutter shell. gn_args['shell_enable_vulkan'] = True # Configure Skia for Vulkan support. gn_args['skia_use_vulkan'] = True - # Enable Metal on non-simulator iOS builds. - if args.target_os == 'ios': - gn_args['skia_use_metal'] = not args.simulator - gn_args['shell_enable_metal'] = not args.simulator - # The buildroot currently isn't set up to support Vulkan in the # Windows ANGLE build, so disable it regardless of enable_vulkan's value. if sys.platform.startswith(('cygwin', 'win')): From c6aebd425dcae2084de0aaa87d470e3abdcb5c0a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 18 Mar 2020 15:21:04 -0400 Subject: [PATCH 034/386] Roll fuchsia/sdk/core/mac-amd64 from AvvHp... to SY5TH... (#17193) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index dda1cc60fc12b..4422fe01e1f30 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'AvvHpw1Mm16GyITH-UL3wOmHL6B0yQNYPb9zmFuTvocC' + 'version': 'SY5THYrN-TQ70o8ZNETBVu1SfaIHgJbTq34q3CiuWngC' } ], 'condition': 'host_os == "mac"', From 15b13530217ac211f7f865a9bdb81a670ca0a8a1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 18 Mar 2020 15:26:02 -0400 Subject: [PATCH 035/386] Roll src/third_party/skia a17558c9f702..d7e0131c7edc (19 commits) (#17194) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 4422fe01e1f30..dfdd05ec2a614 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'a17558c9f70262eace249a4546aff2375d738050', + 'skia_revision': 'd7e0131c7edccfcc777f5d0defee4aa8b2d53ddf', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index aff2b85ed4965..36f1adf187de6 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1d2146938d498ef1d575dbcb48b3a6c4 +Signature: f6ee895fc3ce532637a0356908ecb67f UNUSED LICENSES: @@ -3903,6 +3903,8 @@ FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DBackendContext.h FILE: ../../../third_party/skia/include/private/SkIDChangeListener.h FILE: ../../../third_party/skia/src/core/SkIDChangeListener.cpp FILE: ../../../third_party/skia/src/core/SkVM_fwd.h +FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.cpp +FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.h FILE: ../../../third_party/skia/src/gpu/GrManagedResource.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.h @@ -5167,6 +5169,7 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/skia/docs/examples/50_percent_gray.cpp FILE: ../../../third_party/skia/docs/examples/50_percent_srgb.cpp FILE: ../../../third_party/skia/docs/examples/BlendModes.cpp +FILE: ../../../third_party/skia/docs/examples/Canvas_saveLayer_4.cpp FILE: ../../../third_party/skia/docs/examples/ChromeMDRefreshTab.cpp FILE: ../../../third_party/skia/docs/examples/ChromeMDRefreshTabs.cpp FILE: ../../../third_party/skia/docs/examples/Color_Wheel.cpp @@ -5595,13 +5598,17 @@ FILE: ../../../third_party/skia/gm/verifiers/gmverifier.cpp FILE: ../../../third_party/skia/gm/verifiers/gmverifier.h FILE: ../../../third_party/skia/include/core/SkM44.h FILE: ../../../third_party/skia/modules/skottie/src/Adapter.h -FILE: ../../../third_party/skia/modules/skottie/src/Animator.cpp -FILE: ../../../third_party/skia/modules/skottie/src/Animator.h FILE: ../../../third_party/skia/modules/skottie/src/Camera.cpp FILE: ../../../third_party/skia/modules/skottie/src/Camera.h FILE: ../../../third_party/skia/modules/skottie/src/Path.cpp FILE: ../../../third_party/skia/modules/skottie/src/Transform.cpp FILE: ../../../third_party/skia/modules/skottie/src/Transform.h +FILE: ../../../third_party/skia/modules/skottie/src/animator/Animator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/Animator.h +FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.h +FILE: ../../../third_party/skia/modules/skottie/src/animator/Scalar.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/Vec2.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Ellipse.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/FillStroke.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Gradient.cpp From a28375a5746a2a54fbc033b302323a3bda068471 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 18 Mar 2020 15:31:04 -0400 Subject: [PATCH 036/386] Roll src/third_party/dart 9dfd0127daca..96cf889e6b7b (26 commits) (#17196) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index dfdd05ec2a614..6d8856a87117a 100644 --- a/DEPS +++ b/DEPS @@ -34,11 +34,11 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '9dfd0127daca6e22009235e2a2e5bc9168674884', + 'dart_revision': '96cf889e6b7bd9e596cbd08849c5351a38eec097', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py - 'dart_args_tag': '1.5.3', + 'dart_args_tag': '1.6.0', 'dart_async_tag': '2.4.1', 'dart_bazel_worker_tag': 'v0.1.22', 'dart_boolean_selector_tag': '1.0.4', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index b7f65004e4d6c..70d682e80d639 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: e37c5e14e602cb25f0926be1d16143cb +Signature: b46604502b5e8b62409e2784397b1b16 UNUSED LICENSES: From 498dc773618286ab8d7545642b7d551f33066e32 Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 18 Mar 2020 13:08:54 -0700 Subject: [PATCH 037/386] Truncate error messages for the crash reporter to the maximum length permitted by the FIDL call (#17189) --- .../platform/fuchsia/runtime/dart/utils/handle_exception.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/platform/fuchsia/runtime/dart/utils/handle_exception.cc b/shell/platform/fuchsia/runtime/dart/utils/handle_exception.cc index 6785a1220ed68..3ef03565d904e 100644 --- a/shell/platform/fuchsia/runtime/dart/utils/handle_exception.cc +++ b/shell/platform/fuchsia/runtime/dart/utils/handle_exception.cc @@ -64,6 +64,11 @@ fuchsia::feedback::CrashReport BuildCrashReport( error.substr(delimiter_pos + 2 /*to get rid of the leading ': '*/); } + // Truncate error message to the maximum length allowed for the crash_reporter + // FIDL call + error_message = error_message.substr( + 0, fuchsia::feedback::MAX_EXCEPTION_MESSAGE_LENGTH - 1); + fuchsia::feedback::RuntimeCrashReport dart_report; dart_report.set_exception_type(error_type); dart_report.set_exception_message(error_message); From 857859f8018f9b9b99b0adca978a8198350176f7 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 18 Mar 2020 13:41:41 -0700 Subject: [PATCH 038/386] Remove unused PackagesUri rpc param in OnServiceProtocolRunInView (#17195) This is not actually used by hot reload since we've switched to compilation from kernel. --- shell/common/shell.cc | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/shell/common/shell.cc b/shell/common/shell.cc index b115cb1bf60fa..8dc5cbed29f7c 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1267,16 +1267,6 @@ bool Shell::OnServiceProtocolRunInView( return false; } - // TODO(chinmaygarde): In case of hot-reload from .dill files, the packages - // file is ignored. Currently, the tool is passing a junk packages file to - // pass this check. Update the service protocol interface and remove this - // workaround. - if (params.count("packagesFile") == 0) { - ServiceProtocolParameterError(response, - "'packagesFile' parameter is missing."); - return false; - } - if (params.count("assetDirectory") == 0) { ServiceProtocolParameterError(response, "'assetDirectory' parameter is missing."); @@ -1285,8 +1275,6 @@ bool Shell::OnServiceProtocolRunInView( std::string main_script_path = fml::paths::FromURI(params.at("mainScript").data()); - std::string packages_path = - fml::paths::FromURI(params.at("packagesFile").data()); std::string asset_directory_path = fml::paths::FromURI(params.at("assetDirectory").data()); From bb6c9a343d92b46a7e0abd403b55d142d5d709ff Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 18 Mar 2020 17:36:02 -0400 Subject: [PATCH 039/386] Roll src/third_party/skia d7e0131c7edc..afa83bc20b2c (19 commits) (#17199) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 6d8856a87117a..ecfa194395642 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd7e0131c7edccfcc777f5d0defee4aa8b2d53ddf', + 'skia_revision': 'afa83bc20b2c8da6b776a088d456d54dea34e48b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 36f1adf187de6..a54ea2a6a982b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f6ee895fc3ce532637a0356908ecb67f +Signature: 6cf255d0682e28b0f76b46c2e32ad60b UNUSED LICENSES: @@ -2479,8 +2479,6 @@ FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramBuilder.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLProgramDataManager.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLUniformHandler.h -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLUtil.cpp -FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLUtil.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLVarying.cpp FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLVarying.h FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLXferProcessor.h @@ -5174,6 +5172,7 @@ FILE: ../../../third_party/skia/docs/examples/ChromeMDRefreshTab.cpp FILE: ../../../third_party/skia/docs/examples/ChromeMDRefreshTabs.cpp FILE: ../../../third_party/skia/docs/examples/Color_Wheel.cpp FILE: ../../../third_party/skia/docs/examples/DCIToXYZD50.cpp +FILE: ../../../third_party/skia/docs/examples/GradientShader_MakeLinear.cpp FILE: ../../../third_party/skia/docs/examples/Octopus_Generator.cpp FILE: ../../../third_party/skia/docs/examples/Octopus_Generator_Animated.cpp FILE: ../../../third_party/skia/docs/examples/PaintDump.cpp @@ -5609,6 +5608,7 @@ FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.h FILE: ../../../third_party/skia/modules/skottie/src/animator/Scalar.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/Vec2.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/Vector.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Ellipse.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/FillStroke.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Gradient.cpp From 216c420a2c06e5266a60a768b3fd0b660551cc9c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 18 Mar 2020 19:41:01 -0400 Subject: [PATCH 040/386] Roll src/third_party/skia afa83bc20b2c..d3b92b910ce0 (2 commits) (#17201) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ecfa194395642..dede31d0d4039 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'afa83bc20b2c8da6b776a088d456d54dea34e48b', + 'skia_revision': 'd3b92b910ce0b02e0910dfc07560be8c544da282', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a54ea2a6a982b..f99fefb45427f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6cf255d0682e28b0f76b46c2e32ad60b +Signature: 70d6a185913809ae2063022eb96e83ba UNUSED LICENSES: From 6d300433137815c92c6c961732991d4b7981ffd7 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Wed, 18 Mar 2020 16:47:58 -0700 Subject: [PATCH 041/386] Fix blend test diffrate on mac (#17198) * Fix error message for golden failure. Fix blend test diffrate when running on MacOS * Revert MacOS adjustment --- .../golden_tests/engine/canvas_blend_golden_test.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart index ae316b86992ee..ee05bd37b95aa 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart @@ -19,7 +19,8 @@ void main() async { // Commit a recording canvas to a bitmap, and compare with the expected Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { + {Rect region = const Rect.fromLTWH(0, 0, 500, 500), + double maxDiffRatePercent = 0.0}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); rc.apply(engineCanvas); @@ -29,7 +30,7 @@ void main() async { try { sceneElement.append(engineCanvas.rootElement); html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: 0.0); + await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: maxDiffRatePercent); } finally { // The page is reused across tests, so remove the element after taking the // Scuba screenshot. @@ -76,7 +77,8 @@ void main() async { ..style = PaintingStyle.fill ..color = const Color.fromARGB(128, 255, 0, 0)); rc.restore(); - await _checkScreenshot(rc, 'canvas_blend_circle_diff_color'); + await _checkScreenshot(rc, 'canvas_blend_circle_diff_color', + maxDiffRatePercent: operatingSystem == OperatingSystem.macOs ? 2.95 : 0); }); test('Blend circle and text with multiply', () async { @@ -112,7 +114,8 @@ void main() async { rc.drawImage(createTestImage(), Offset(135.0, 130.0), Paint()..blendMode = BlendMode.multiply); rc.restore(); - await _checkScreenshot(rc, 'canvas_blend_image_multiply'); + await _checkScreenshot(rc, 'canvas_blend_image_multiply', + maxDiffRatePercent: operatingSystem == OperatingSystem.macOs ? 2.95 : 0); }); } From 373ebe6430b227c48cc2dd7ab860f69462eea590 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 19 Mar 2020 11:04:43 -0700 Subject: [PATCH 042/386] Fix copy/paste mistake in GLFW asset path resolution (#17208) Fixes a regression from #16944 --- shell/platform/glfw/flutter_glfw.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index f79c360fa88d1..fa359076c71f6 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -526,7 +526,7 @@ static FLUTTER_API_SYMBOL(FlutterEngine) icu_path = std::filesystem::path(executable_location) / icu_path; } std::string assets_path_string = assets_path.u8string(); - std::string icu_path_string = assets_path.u8string(); + std::string icu_path_string = icu_path.u8string(); FlutterRendererConfig config = {}; if (window == nullptr) { From b697cdc961d1cb5088b9fccb0ccf9cede76fc554 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 19 Mar 2020 14:31:08 -0400 Subject: [PATCH 043/386] Roll src/third_party/dart 96cf889e6b7b..5b21a43c556c (18 commits) (#17202) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index dede31d0d4039..521687ec46cfd 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '96cf889e6b7bd9e596cbd08849c5351a38eec097', + 'dart_revision': '5b21a43c556c55ee788aee4fdbd8563a7fe329ab', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 70d682e80d639..0c4b0e679aed7 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: b46604502b5e8b62409e2784397b1b16 +Signature: fe06cabd4e8412d069b983c21f83eeaf UNUSED LICENSES: From 344d57aec98250b3d53138a7fc9022eb84d1c94b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 19 Mar 2020 14:36:04 -0400 Subject: [PATCH 044/386] Roll fuchsia/sdk/core/linux-amd64 from p9B8q... to de6Ca... (#17204) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 521687ec46cfd..f0acb65b720fc 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'p9B8qVYSEoGGPvRk2-f7kF3QQ9J8WXc3O9y3_BF_dmwC' + 'version': 'de6Cay9W1IQjrymnXO7UTNxZ09pJ_MPojV2ykS-eFXEC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 43cd98ee38927..8d01bb4d55583 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 02005f102f898d40cb6578d777a11227 +Signature: a994d936b5b14118ed7f2ab08b853e35 UNUSED LICENSES: From 16e2d7503bb521ffe4d617bc546736eb6dc65def Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 19 Mar 2020 14:41:02 -0400 Subject: [PATCH 045/386] Roll src/third_party/skia d3b92b910ce0..d65a790d71e8 (1 commits) (#17205) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f0acb65b720fc..1ea8e6d15d3b5 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd3b92b910ce0b02e0910dfc07560be8c544da282', + 'skia_revision': 'd65a790d71e82d0e8aa3fa9ade55db5e0ef09240', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f99fefb45427f..09515b0fafd3d 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 70d6a185913809ae2063022eb96e83ba +Signature: 078dc19548908f888f36eae860dbc5de UNUSED LICENSES: From 8a5c8c3eb12533288fba077b23d507ec412d5f3a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 19 Mar 2020 14:46:03 -0400 Subject: [PATCH 046/386] Roll fuchsia/sdk/core/mac-amd64 from SY5TH... to K9kFP... (#17207) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 1ea8e6d15d3b5..1007315cbf3b6 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'SY5THYrN-TQ70o8ZNETBVu1SfaIHgJbTq34q3CiuWngC' + 'version': 'K9kFPuT_Efu32cXzsfADv2wVmOWOcJiGxGYCgOyOgUIC' } ], 'condition': 'host_os == "mac"', From 06c8a2909e4205d2e845fb948f364521229add51 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 19 Mar 2020 16:56:03 -0400 Subject: [PATCH 047/386] Roll src/third_party/skia d65a790d71e8..1171d314efc7 (6 commits) (#17212) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1007315cbf3b6..39dca50ce82ff 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd65a790d71e82d0e8aa3fa9ade55db5e0ef09240', + 'skia_revision': '1171d314efc7e131b039c94126d26575cfb9e70c', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 09515b0fafd3d..81c5c4d95c038 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 078dc19548908f888f36eae860dbc5de +Signature: 3853cd4fdb9a7fb4720fa083bfffecc9 UNUSED LICENSES: From 046a4a6c2bc36c3f91a1713cf1fb75e3636e5227 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 19 Mar 2020 19:16:02 -0400 Subject: [PATCH 048/386] Roll src/third_party/skia 1171d314efc7..4b55ff097b9d (2 commits) (#17214) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 39dca50ce82ff..70c71e8060e55 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '1171d314efc7e131b039c94126d26575cfb9e70c', + 'skia_revision': '4b55ff097b9d61ab9d4ebfbac2417996fb4fa5f0', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 81c5c4d95c038..29462e426de0a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 3853cd4fdb9a7fb4720fa083bfffecc9 +Signature: ee7fca1dfefea1d0c616db5e7ab76b80 UNUSED LICENSES: From b1af234b74bbc05dcf89b4ea97711a276b127d2a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 19 Mar 2020 18:13:40 -0700 Subject: [PATCH 049/386] Disable the use of the API_AVAILABLE macro in Skia in iOS release builds. (#17213) The use of this macro in C++ translation units leads to the symbol visibility being switched to default instead of hidden. This only happens in bitcode enabled builds. A toolchain upgrade will be necessary to undo this workaround. --- tools/gn | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/gn b/tools/gn index b7cb71763da95..0fb187febda35 100755 --- a/tools/gn +++ b/tools/gn @@ -222,7 +222,10 @@ def to_gn_args(args): if args.enable_metal: gn_args['skia_use_metal'] = True gn_args['shell_enable_metal'] = True - gn_args['allow_deprecated_api_calls'] = True + # Bitcode enabled builds using the current version of the toolchain leak + # C++ symbols decorated with the availability attribute. Disable these + # attributes in release modes till the toolchain is updated. + gn_args['skia_enable_api_available_macro'] = args.runtime_mode != "release" if args.enable_vulkan: # Enable vulkan in the Flutter shell. From 8581937c49d6b63be61d9596012d29293a9271d3 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 19 Mar 2020 19:46:02 -0700 Subject: [PATCH 050/386] Enable unified OpenGL/Metal builds. (#17217) --- tools/gn | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/gn b/tools/gn index 0fb187febda35..6d47127ca30fe 100755 --- a/tools/gn +++ b/tools/gn @@ -44,9 +44,6 @@ def get_out_dir(args): if args.enable_vulkan: target_dir.append('vulkan') - if args.enable_metal and args.target_os == 'ios': - target_dir.append('metal') - return os.path.join(args.out_dir, 'out', '_'.join(target_dir)) def to_command_line(gn_args): @@ -219,7 +216,8 @@ def to_gn_args(args): gn_args['use_goma'] = False gn_args['goma_dir'] = None - if args.enable_metal: + # Enable Metal on non-simulator iOS builds. + if args.target_os == 'ios' and not args.simulator: gn_args['skia_use_metal'] = True gn_args['shell_enable_metal'] = True # Bitcode enabled builds using the current version of the toolchain leak @@ -323,7 +321,6 @@ def parse_args(args): parser.add_argument('--operator-new-alignment', dest='operator_new_alignment', type=str, default=None) parser.add_argument('--enable-vulkan', action='store_true', default=False) - parser.add_argument('--enable-metal', action='store_true', default=False) parser.add_argument('--enable-fontconfig', action='store_true', default=False) parser.add_argument('--enable-skshaper', action='store_true', default=False) From fd60ddf7517ce2bfd6577636bda2065c0e492664 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 19 Mar 2020 20:38:22 -0700 Subject: [PATCH 051/386] Revert "Enable unified OpenGL/Metal builds. (#17217)" (#17218) This reverts commit 8581937c49d6b63be61d9596012d29293a9271d3. --- tools/gn | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/gn b/tools/gn index 6d47127ca30fe..0fb187febda35 100755 --- a/tools/gn +++ b/tools/gn @@ -44,6 +44,9 @@ def get_out_dir(args): if args.enable_vulkan: target_dir.append('vulkan') + if args.enable_metal and args.target_os == 'ios': + target_dir.append('metal') + return os.path.join(args.out_dir, 'out', '_'.join(target_dir)) def to_command_line(gn_args): @@ -216,8 +219,7 @@ def to_gn_args(args): gn_args['use_goma'] = False gn_args['goma_dir'] = None - # Enable Metal on non-simulator iOS builds. - if args.target_os == 'ios' and not args.simulator: + if args.enable_metal: gn_args['skia_use_metal'] = True gn_args['shell_enable_metal'] = True # Bitcode enabled builds using the current version of the toolchain leak @@ -321,6 +323,7 @@ def parse_args(args): parser.add_argument('--operator-new-alignment', dest='operator_new_alignment', type=str, default=None) parser.add_argument('--enable-vulkan', action='store_true', default=False) + parser.add_argument('--enable-metal', action='store_true', default=False) parser.add_argument('--enable-fontconfig', action='store_true', default=False) parser.add_argument('--enable-skshaper', action='store_true', default=False) From 8dbb8ca70b9ffa90fe7e3e75d121f57f15be5789 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 00:56:01 -0400 Subject: [PATCH 052/386] Roll src/third_party/dart 5b21a43c556c..1bab476834dc (41 commits) (#17219) --- DEPS | 6 +++--- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 70c71e8060e55..9e8a8cbb8c600 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '5b21a43c556c55ee788aee4fdbd8563a7fe329ab', + 'dart_revision': '1bab476834dcfc19707d373d0bcb5e0b17a7c665', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -88,7 +88,7 @@ vars = { 'dart_shelf_web_socket_tag': '0.2.2+3', 'dart_source_map_stack_trace_tag': '2.0.0', 'dart_source_maps_tag': '8af7cc1a1c3a193c1fba5993ce22a546a319c40e', - 'dart_source_span_tag': '1.5.5', + 'dart_source_span_tag': '1.7.0', 'dart_stack_trace_tag': '1.9.3', 'dart_stagehand_tag': 'v3.3.7', 'dart_stream_channel_tag': '2.0.0', @@ -100,7 +100,7 @@ vars = { 'dart_typed_data_tag': '1.1.6', 'dart_usage_tag': '3.4.0', 'dart_watcher_rev': '0.9.7+14', - 'dart_web_socket_channel_tag': '1.0.9', + 'dart_web_socket_channel_tag': '1.0.15', 'dart_yaml_tag': '2.2.0', 'ocmock_tag': 'v3.4.3', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 0c4b0e679aed7..4ccdf267b2ce3 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: fe06cabd4e8412d069b983c21f83eeaf +Signature: bb7475da375e6d3d09b02525bab95d8b UNUSED LICENSES: From 45a910fb13b96b0e172e5d3f10f4d774d203bf84 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 05:16:05 -0400 Subject: [PATCH 053/386] Roll fuchsia/sdk/core/mac-amd64 from K9kFP... to MKYwg... (#17220) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 9e8a8cbb8c600..81ba90e357e72 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'K9kFPuT_Efu32cXzsfADv2wVmOWOcJiGxGYCgOyOgUIC' + 'version': 'MKYwg75UljQZXx15HwzYoQj3Vxa0KVJ7q1x0XoALCBQC' } ], 'condition': 'host_os == "mac"', From 4334a241509ab52f6da9da610d3c4475d271a6ba Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 05:21:02 -0400 Subject: [PATCH 054/386] Roll fuchsia/sdk/core/linux-amd64 from de6Ca... to ZmlfS... (#17221) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 81ba90e357e72..bc015d59a8957 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'de6Cay9W1IQjrymnXO7UTNxZ09pJ_MPojV2ykS-eFXEC' + 'version': 'ZmlfSUYgB02RndQVLenzGehstcM7ASCSaD9L8DETVX8C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 8d01bb4d55583..75fcccf9db28a 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: a994d936b5b14118ed7f2ab08b853e35 +Signature: 8d4a75deacb75655c9faf5686550c648 UNUSED LICENSES: @@ -3269,6 +3269,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/event.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.legacymetrics/metrics_recorder.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.target/target_discovery.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/activity_reporter.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/profile_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/memorypressure.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/night_mode.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/events.fidl From 3bd89361ad09ff1ddcc1e5012ef47c563b86a9d9 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 05:26:02 -0400 Subject: [PATCH 055/386] Roll src/third_party/dart 1bab476834dc..532a602fefc7 (4 commits) (#17222) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index bc015d59a8957..3ff89ab415c24 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '1bab476834dcfc19707d373d0bcb5e0b17a7c665', + 'dart_revision': '532a602fefc7f16f8b3877c261a7bbe967b5b2d2', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 4200339ff1b0baad665aedb386a3c64cfd3e80fc Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 10:06:03 -0400 Subject: [PATCH 056/386] Roll src/third_party/dart 532a602fefc7..ba8baa46b452 (3 commits) (#17223) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 3ff89ab415c24..891f402624834 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '532a602fefc7f16f8b3877c261a7bbe967b5b2d2', + 'dart_revision': 'ba8baa46b4527a631c077f5a59c348931688d91a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 6aedbb7d3454b8d20c02b9b9b9fd5de31d438093 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 10:51:02 -0400 Subject: [PATCH 057/386] Roll src/third_party/skia 4b55ff097b9d..f22744971516 (1 commits) (#17224) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 891f402624834..26934fa917f2a 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4b55ff097b9d61ab9d4ebfbac2417996fb4fa5f0', + 'skia_revision': 'f22744971516f4e11103bd5c8230f1a5b9068dab', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 29462e426de0a..93969878ffa59 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ee7fca1dfefea1d0c616db5e7ab76b80 +Signature: 904f7547c849f55dfe0cf003336285cc UNUSED LICENSES: From 77e6d32a1ebb5c235bd92b0176ee155ded24b2d4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 13:01:02 -0400 Subject: [PATCH 058/386] Roll src/third_party/skia f22744971516..85755f46a881 (1 commits) (#17225) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 26934fa917f2a..5412edddbd725 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f22744971516f4e11103bd5c8230f1a5b9068dab', + 'skia_revision': '85755f46a8810b1863493a81887f1dc17c2e49e1', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 93969878ffa59..4529bec3a45d3 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 904f7547c849f55dfe0cf003336285cc +Signature: 83245e49e829e1b31e83ac42cc773a33 UNUSED LICENSES: From 2d42c74a348d98d2fd372a91953c104e58f185cd Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 20 Mar 2020 11:04:47 -0700 Subject: [PATCH 059/386] Replace Pipeline's `ProduceToFront` with `ProduceIfEmpty` to handle thread merging. (#17122) --- shell/common/pipeline.h | 35 +++++++++++++++++++----------- shell/common/pipeline_unittests.cc | 28 +++++++----------------- shell/common/rasterizer.cc | 3 +-- 3 files changed, 31 insertions(+), 35 deletions(-) diff --git a/shell/common/pipeline.h b/shell/common/pipeline.h index 587c81ff6fbc2..296d9bf4a87ab 100644 --- a/shell/common/pipeline.h +++ b/shell/common/pipeline.h @@ -111,16 +111,22 @@ class Pipeline : public fml::RefCountedThreadSafe> { GetNextPipelineTraceID()}; // trace id } - // Pushes task to the front of the pipeline. - // - // If we exceed the depth completing this continuation, we drop the - // last frame to preserve the depth of the pipeline. - // - // Note: Use |Pipeline::Produce| where possible. This should only be - // used to en-queue high-priority resources. - ProducerContinuation ProduceToFront() { + // Create a `ProducerContinuation` that will only push the task if the queue + // is empty. + // Prefer using |Produce|. ProducerContinuation returned by this method + // doesn't guarantee that the frame will be rendered. + ProducerContinuation ProduceIfEmpty() { + if (!empty_.TryWait()) { + return {}; + } + ++inflight_; + FML_TRACE_COUNTER("flutter", "Pipeline Depth", + reinterpret_cast(this), // + "frames in flight", inflight_.load() // + ); + return ProducerContinuation{ - std::bind(&Pipeline::ProducerCommitFront, this, std::placeholders::_1, + std::bind(&Pipeline::ProducerCommitIfEmpty, this, std::placeholders::_1, std::placeholders::_2), // continuation GetNextPipelineTraceID()}; // trace id } @@ -181,13 +187,16 @@ class Pipeline : public fml::RefCountedThreadSafe> { available_.Signal(); } - void ProducerCommitFront(ResourcePtr resource, size_t trace_id) { + void ProducerCommitIfEmpty(ResourcePtr resource, size_t trace_id) { { std::scoped_lock lock(queue_mutex_); - queue_.emplace_front(std::move(resource), trace_id); - while (queue_.size() > depth_) { - queue_.pop_back(); + if (!queue_.empty()) { + // Bail if the queue is not empty, opens up spaces to produce other + // frames. + empty_.Signal(); + return; } + queue_.emplace_back(std::move(resource), trace_id); } // Ensure the queue mutex is not held as that would be a pessimization. diff --git a/shell/common/pipeline_unittests.cc b/shell/common/pipeline_unittests.cc index ada908953b45a..76fdc85867ee7 100644 --- a/shell/common/pipeline_unittests.cc +++ b/shell/common/pipeline_unittests.cc @@ -89,46 +89,34 @@ TEST(PipelineTest, PushingMultiProcessesInOrder) { ASSERT_EQ(consume_result_2, PipelineConsumeResult::Done); } -TEST(PipelineTest, PushingToFrontOverridesOrder) { +TEST(PipelineTest, ProduceIfEmptyDoesNotConsumeWhenQueueIsNotEmpty) { const int depth = 2; fml::RefPtr pipeline = fml::MakeRefCounted(depth); Continuation continuation_1 = pipeline->Produce(); - Continuation continuation_2 = pipeline->ProduceToFront(); + Continuation continuation_2 = pipeline->ProduceIfEmpty(); const int test_val_1 = 1, test_val_2 = 2; continuation_1.Complete(std::make_unique(test_val_1)); continuation_2.Complete(std::make_unique(test_val_2)); PipelineConsumeResult consume_result_1 = pipeline->Consume( - [&test_val_2](std::unique_ptr v) { ASSERT_EQ(*v, test_val_2); }); - ASSERT_EQ(consume_result_1, PipelineConsumeResult::MoreAvailable); - - PipelineConsumeResult consume_result_2 = pipeline->Consume( [&test_val_1](std::unique_ptr v) { ASSERT_EQ(*v, test_val_1); }); - ASSERT_EQ(consume_result_2, PipelineConsumeResult::Done); + ASSERT_EQ(consume_result_1, PipelineConsumeResult::Done); } -TEST(PipelineTest, PushingToFrontDropsLastResource) { - const int depth = 2; +TEST(PipelineTest, ProduceIfEmptySuccessfulIfQueueIsEmpty) { + const int depth = 1; fml::RefPtr pipeline = fml::MakeRefCounted(depth); - Continuation continuation_1 = pipeline->Produce(); - Continuation continuation_2 = pipeline->Produce(); - Continuation continuation_3 = pipeline->ProduceToFront(); + Continuation continuation_1 = pipeline->ProduceIfEmpty(); - const int test_val_1 = 1, test_val_2 = 2, test_val_3 = 3; + const int test_val_1 = 1; continuation_1.Complete(std::make_unique(test_val_1)); - continuation_2.Complete(std::make_unique(test_val_2)); - continuation_3.Complete(std::make_unique(test_val_3)); PipelineConsumeResult consume_result_1 = pipeline->Consume( - [&test_val_3](std::unique_ptr v) { ASSERT_EQ(*v, test_val_3); }); - ASSERT_EQ(consume_result_1, PipelineConsumeResult::MoreAvailable); - - PipelineConsumeResult consume_result_2 = pipeline->Consume( [&test_val_1](std::unique_ptr v) { ASSERT_EQ(*v, test_val_1); }); - ASSERT_EQ(consume_result_2, PipelineConsumeResult::Done); + ASSERT_EQ(consume_result_1, PipelineConsumeResult::Done); } } // namespace testing diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index acad5ada8510a..811898b5a42d2 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -127,9 +127,8 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { // if the raster status is to resubmit the frame, we push the frame to the // front of the queue and also change the consume status to more available. if (raster_status == RasterStatus::kResubmit) { - auto front_continuation = pipeline->ProduceToFront(); + auto front_continuation = pipeline->ProduceIfEmpty(); front_continuation.Complete(std::move(resubmitted_layer_tree_)); - consume_result = PipelineConsumeResult::MoreAvailable; } else if (raster_status == RasterStatus::kEnqueuePipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } From 6c757f3bba59fb86b6dc0dcd96266e06eec562f0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 15:11:01 -0400 Subject: [PATCH 060/386] Roll src/third_party/dart ba8baa46b452..0296286c03f6 (11 commits) (#17226) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 5412edddbd725..559bb386202de 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'ba8baa46b4527a631c077f5a59c348931688d91a', + 'dart_revision': '0296286c03f6d7d9db272fc12a1e0c1a4957c69f', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -77,7 +77,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '429a06039d185149f387a65e3503b0693ce6d24e', + 'dart_pub_rev': '11afdac99fe67373829f37acc75b625011d360ad', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 4ccdf267b2ce3..332c21e291898 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: bb7475da375e6d3d09b02525bab95d8b +Signature: 751019019a61cc75137e78d2f4da5f02 UNUSED LICENSES: From fdf4f7883f678eb6d5c30c303c485fae1f8ba0cb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 15:16:01 -0400 Subject: [PATCH 061/386] Roll src/third_party/skia 85755f46a881..7b7b78cb9f1b (6 commits) (#17227) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index 559bb386202de..f515a615d4e1b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '85755f46a8810b1863493a81887f1dc17c2e49e1', + 'skia_revision': '7b7b78cb9f1bd8b57fb849e5cae7f477e84d3cf6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 4529bec3a45d3..534df3431875d 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 83245e49e829e1b31e83ac42cc773a33 +Signature: 1abbcf1f0b0817a1a80805bd2b8db4d9 UNUSED LICENSES: @@ -1012,7 +1012,6 @@ FILE: ../../../third_party/skia/infra/bots/assets/linux_vulkan_sdk/VERSION FILE: ../../../third_party/skia/infra/bots/assets/lottie-samples/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mesa_intel_driver_linux/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mesa_intel_driver_linux/mesa-driver-builder/Dockerfile -FILE: ../../../third_party/skia/infra/bots/assets/moltenvk/VERSION FILE: ../../../third_party/skia/infra/bots/assets/mskp/VERSION FILE: ../../../third_party/skia/infra/bots/assets/node/VERSION FILE: ../../../third_party/skia/infra/bots/assets/opencl_headers/VERSION @@ -1090,7 +1089,6 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Debug-Metal.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-x86_64-Release-MoltenVK_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-arm64-Release-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86-Debug-Exceptions.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Debug-ANGLE.json @@ -3896,14 +3894,16 @@ TYPE: LicenseType.bsd FILE: ../../../third_party/skia/bench/GrQuadBench.cpp FILE: ../../../third_party/skia/gm/crbug_1041204.cpp FILE: ../../../third_party/skia/gm/crbug_224618.cpp -FILE: ../../../third_party/skia/include/gpu/d3d/GrD3D12.h FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DBackendContext.h +FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DTypes.h +FILE: ../../../third_party/skia/include/private/GrD3DTypesPriv.h FILE: ../../../third_party/skia/include/private/SkIDChangeListener.h FILE: ../../../third_party/skia/src/core/SkIDChangeListener.cpp FILE: ../../../third_party/skia/src/core/SkVM_fwd.h FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.cpp FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.h FILE: ../../../third_party/skia/src/gpu/GrManagedResource.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3D12.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.cpp @@ -3915,6 +3915,7 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResource.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceState.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTypesPriv.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.h FILE: ../../../third_party/skia/src/gpu/effects/GrDeviceSpaceEffect.fp From 49f8dba82933194e29d196b84cea8ef4b6556aac Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 20 Mar 2020 12:33:02 -0700 Subject: [PATCH 062/386] Sync Cirrus web test config from flutter/flutter (#17228) --- .cirrus.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 95bc2e9cc5384..98ff5be33f3a9 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -3,9 +3,9 @@ gcp_credentials: ENCRYPTED[987a78af29b91ce8489594c9ab3fec21845bbe5ba68294b8f6def web_shard_template: &WEB_SHARD_TEMPLATE only_if: "changesInclude('.cirrus.yml', 'lib/web_ui/**', 'web_sdk/**') || $CIRRUS_PR == ''" environment: - # As of October 2019, the Web shards needed more than 6G of RAM. - CPU: 2 - MEMORY: 8G + # As of March 2020, the Web shards needed 16G of RAM and 4 CPUs to run all framework tests with goldens without flaking. + CPU: 4 + MEMORY: 16G compile_host_script: | cd $ENGINE_PATH/src ./flutter/tools/gn --unoptimized --full-dart-sdk From 2627634be807a28f4d56c5d7cd878c3ff6ce07ac Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 20 Mar 2020 12:38:58 -0700 Subject: [PATCH 063/386] Implement unobstructed Platform Views on iOS (#17049) --- flow/embedded_views.cc | 5 +- flow/embedded_views.h | 5 +- flow/layers/picture_layer.cc | 2 +- flow/layers/picture_layer_unittests.cc | 3 - shell/common/rasterizer.cc | 12 +- .../framework/Source/FlutterPlatformViews.mm | 320 ++++++++++++------ .../Source/FlutterPlatformViews_Internal.h | 87 ++++- .../Source/FlutterPlatformViews_Internal.mm | 3 +- shell/platform/darwin/ios/ios_surface.h | 5 +- shell/platform/darwin/ios/ios_surface.mm | 12 +- .../embedder_external_view_embedder.cc | 6 +- .../embedder_external_view_embedder.h | 5 +- .../Scenarios.xcodeproj/project.pbxproj | 4 + .../ios/Scenarios/Scenarios/AppDelegate.m | 7 + .../UnobstructedPlatformViewTests.m | 254 ++++++++++++++ ...tform_view_clippath_iPhone 8_simulator.png | Bin 30022 -> 20295 bytes ...form_view_transform_iPhone 8_simulator.png | Bin 33727 -> 22360 bytes testing/scenario_app/lib/main.dart | 6 + .../scenario_app/lib/src/platform_view.dart | 227 ++++++++++++- 19 files changed, 839 insertions(+), 124 deletions(-) create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index c660f4691318b..5234bf1e50c8c 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -6,10 +6,13 @@ namespace flutter { -bool ExternalViewEmbedder::SubmitFrame(GrContext* context) { +bool ExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { return false; }; +void ExternalViewEmbedder::FinishFrame(){}; + void MutatorsStack::PushClipRect(const SkRect& rect) { std::shared_ptr element = std::make_shared(rect); vector_.push_back(element); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 030eb88c8a06d..7a491a8a152ef 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -248,7 +248,10 @@ class ExternalViewEmbedder { // Must be called on the UI thread. virtual SkCanvas* CompositeEmbeddedView(int view_id) = 0; - virtual bool SubmitFrame(GrContext* context); + virtual bool SubmitFrame(GrContext* context, SkCanvas* background_canvas); + + // This is called after submitting the embedder frame and the surface frame. + virtual void FinishFrame(); FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 3bc7e394c1033..08c09cc9e833b 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -59,7 +59,7 @@ void PictureLayer::Paint(PaintContext& context) const { return; } } - context.leaf_nodes_canvas->drawPicture(picture()); + picture()->playback(context.leaf_nodes_canvas); } } // namespace flutter diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 687c870eeac66..4f565cf500ecc 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -94,9 +94,6 @@ TEST_F(PictureLayerTest, SimplePicture) { 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( layer_offset_matrix)}}, #endif - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), - SkMatrix()}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 811898b5a42d2..f7e4350fe9c97 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -342,9 +342,17 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { if (raster_status == RasterStatus::kFailed) { return raster_status; } - frame->Submit(); if (external_view_embedder != nullptr) { - external_view_embedder->SubmitFrame(surface_->GetContext()); + external_view_embedder->SubmitFrame(surface_->GetContext(), + root_surface_canvas); + // The external view embedder may mutate the root surface canvas while + // submitting the frame. + // Therefore, submit the final frame after asking the external view + // embedder to submit the frame. + frame->Submit(); + external_view_embedder->FinishFrame(); + } else { + frame->Submit(); } FireNextFrameCallbackIfPresent(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b86c4623fa7a7..b96fcbbbaa76a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -8,16 +8,77 @@ #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include #include #include #include #include "FlutterPlatformViews_Internal.h" +#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" namespace flutter { +std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( + GrContext* gr_context, + std::shared_ptr ios_context) { + if (available_layer_index_ >= layers_.size()) { + std::shared_ptr layer; + + if (!gr_context) { + fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + + layer = std::make_shared( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + } else { + CGFloat screenScale = [UIScreen mainScreen].scale; + fml::scoped_nsobject overlay_view( + [[FlutterOverlayView alloc] initWithContentsScale:screenScale]); + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + + layer = std::make_shared( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + layer->gr_context = gr_context; + } + layers_.push_back(layer); + } + auto layer = layers_[available_layer_index_]; + if (gr_context != layer->gr_context) { + layer->gr_context = gr_context; + // The overlay already exists, but the GrContext was changed so we need to recreate + // the rendering surface with the new GrContext. + IOSSurface* ios_surface = layer->ios_surface.get(); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + layer->surface = std::move(surface); + } + available_layer_index_++; + return layer; +} + +void FlutterPlatformViewLayerPool::RecycleLayers() { + available_layer_index_ = 0; +} + +std::vector> +FlutterPlatformViewLayerPool::GetUnusedLayers() { + std::vector> results; + for (size_t i = available_layer_index_; i < layers_.size(); i++) { + results.push_back(layers_[i]); + } + return results; +} + void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { flutter_view_.reset([flutter_view retain]); } @@ -83,6 +144,9 @@ NSObject* embedded_view = [factory createWithFrame:CGRectZero viewIdentifier:viewId arguments:params]; + // Set a unique view identifier, so the platform view can be identified in unit tests. + [embedded_view view].accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%ld]", viewId]; views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] @@ -196,8 +260,11 @@ int view_id, std::unique_ptr params) { picture_recorders_[view_id] = std::make_unique(); - picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_)); - picture_recorders_[view_id]->getRecordingCanvas()->clear(SK_ColorTRANSPARENT); + + auto rtree_factory = RTreeFactory(); + platform_view_rtrees_[view_id] = rtree_factory.getInstance(); + picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_), &rtree_factory); + composition_order_.push_back(view_id); if (current_composition_params_.count(view_id) == 1 && @@ -361,77 +428,182 @@ composition_order_.clear(); active_composition_order_.clear(); picture_recorders_.clear(); + platform_view_rtrees_.clear(); current_composition_params_.clear(); clip_count_.clear(); views_to_recomposite_.clear(); + layer_pool_->RecycleLayers(); +} + +SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) { + UIView* platform_view = [views_[view_id].get() view]; + UIScreen* screen = [UIScreen mainScreen]; + CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds + toView:flutter_view_]; + return SkRect::MakeXYWH(platform_view_cgrect.origin.x * screen.scale, // + platform_view_cgrect.origin.y * screen.scale, // + platform_view_cgrect.size.width * screen.scale, // + platform_view_cgrect.size.height * screen.scale // + ); } bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context) { + std::shared_ptr ios_context, + SkCanvas* background_canvas) { DisposeViews(); - - bool did_submit = true; - for (int64_t view_id : composition_order_) { - EnsureOverlayInitialized(view_id, ios_context, gr_context); - auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); - // If frame is null, AcquireFrame already printed out an error message. - if (frame) { - SkCanvas* canvas = frame->SkiaCanvas(); - canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); - canvas->flush(); - did_submit &= frame->Submit(); + // Clipping the background canvas before drawing the picture recorders requires to + // save and restore the clip context. + SkAutoCanvasRestore save(background_canvas, /*doSave=*/true); + // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. + LayersMap platform_view_layers; + + auto did_submit = true; + auto num_platform_views = composition_order_.size(); + + for (size_t i = 0; i < num_platform_views; i++) { + int64_t platform_view_id = composition_order_[i]; + sk_sp rtree = platform_view_rtrees_[platform_view_id]; + sk_sp picture = picture_recorders_[platform_view_id]->finishRecordingAsPicture(); + + // Check if the current picture contains overlays that intersect with the + // current platform view or any of the previous platform views. + for (size_t j = i + 1; j > 0; j--) { + int64_t current_platform_view_id = composition_order_[j - 1]; + SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); + std::list intersection_rects = + rtree->searchNonOverlappingDrawnRects(platform_view_rect); + auto allocation_size = intersection_rects.size(); + + // For testing purposes, the overlay id is used to find the overlay view. + // This is the index of the layer for the current platform view. + auto overlay_id = platform_view_layers[current_platform_view_id].size(); + + // If the max number of allocations per platform view is exceeded, + // then join all the rects into a single one. + // + // TODO(egarciad): Consider making this configurable. + // https://github.com/flutter/flutter/issues/52510 + if (allocation_size > kMaxLayerAllocations) { + SkRect joined_rect; + for (const SkRect& rect : intersection_rects) { + joined_rect.join(rect); + } + // Replace the rects in the intersection rects list for a single rect that is + // the union of all the rects in the list. + intersection_rects.clear(); + intersection_rects.push_back(joined_rect); + } + for (SkRect& joined_rect : intersection_rects) { + // Get the intersection rect between the current rect + // and the platform view rect. + joined_rect.intersect(platform_view_rect); + // Clip the background canvas, so it doesn't contain any of the pixels drawn + // on the overlay layer. + background_canvas->clipRect(joined_rect, SkClipOp::kDifference); + // Get a new host layer. + auto layer = GetLayer(gr_context, // + ios_context, // + picture, // + joined_rect, // + current_platform_view_id, // + overlay_id // + ); + did_submit &= layer->did_submit_last_frame; + platform_view_layers[current_platform_view_id].push_back(layer); + overlay_id++; + } } - } - picture_recorders_.clear(); - if (composition_order_ == active_composition_order_) { - composition_order_.clear(); - return did_submit; - } - DetachUnusedLayers(); - active_composition_order_.clear(); - UIView* flutter_view = flutter_view_.get(); + background_canvas->drawPicture(picture); + } + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(); + // Organize the layers by their z indexes. + BringLayersIntoView(platform_view_layers); + // Mark all layers as available, so they can be used in the next frame. + layer_pool_->RecycleLayers(); + // Reset the composition order, so next frame starts empty. + composition_order_.clear(); + + return did_submit; +} +void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { + UIView* flutter_view = flutter_view_.get(); + auto zIndex = 0; for (size_t i = 0; i < composition_order_.size(); i++) { - int view_id = composition_order_[i]; - // We added a chain of super views to the platform view to handle clipping. - // The `platform_view_root` is the view at the top of the chain which is a direct subview of the - // `FlutterView`. - UIView* platform_view_root = root_views_[view_id].get(); - UIView* overlay = overlays_[view_id]->overlay_view; - FML_CHECK(platform_view_root.superview == overlay.superview); - if (platform_view_root.superview == flutter_view) { - [flutter_view bringSubviewToFront:platform_view_root]; - [flutter_view bringSubviewToFront:overlay]; - } else { + int64_t platform_view_id = composition_order_[i]; + std::vector> layers = layer_map[platform_view_id]; + UIView* platform_view_root = root_views_[platform_view_id].get(); + + if (platform_view_root.superview != flutter_view) { [flutter_view addSubview:platform_view_root]; - [flutter_view addSubview:overlay]; - overlay.frame = flutter_view.bounds; + } else { + platform_view_root.layer.zPosition = zIndex++; } - - active_composition_order_.push_back(view_id); + for (const std::shared_ptr& layer : layers) { + if ([layer->overlay_view superview] != flutter_view) { + [flutter_view addSubview:layer->overlay_view]; + } else { + layer->overlay_view.get().layer.zPosition = zIndex++; + } + } + active_composition_order_.push_back(platform_view_id); } - composition_order_.clear(); - return did_submit; } -void FlutterPlatformViewsController::DetachUnusedLayers() { +std::shared_ptr FlutterPlatformViewsController::GetLayer( + GrContext* gr_context, + std::shared_ptr ios_context, + sk_sp picture, + SkRect rect, + int64_t view_id, + int64_t overlay_id) { + auto layer = layer_pool_->GetLayer(gr_context, ios_context); + auto screenScale = [UIScreen mainScreen].scale; + // Set the size of the overlay UIView. + layer->overlay_view.get().frame = CGRectMake(rect.x() / screenScale, // + rect.y() / screenScale, // + rect.width() / screenScale, // + rect.height() / screenScale // + ); + // Set a unique view identifier, so the overlay can be identified in unit tests. + layer->overlay_view.get().accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; + + std::unique_ptr frame = + layer->surface->AcquireFrame(SkISize::Make(rect.width(), rect.height())); + // If frame is null, AcquireFrame already printed out an error message. + if (!frame) { + return layer; + } + auto overlay_canvas = frame->SkiaCanvas(); + overlay_canvas->clear(SK_ColorTRANSPARENT); + // Offset the picture since its absolute position on the scene is determined + // by the position of the overlay view. + overlay_canvas->translate(-rect.x(), -rect.y()); + overlay_canvas->drawPicture(picture); + + layer->did_submit_last_frame = frame->Submit(); + return layer; +} + +void FlutterPlatformViewsController::RemoveUnusedLayers() { + auto layers = layer_pool_->GetUnusedLayers(); + for (const std::shared_ptr& layer : layers) { + [layer->overlay_view removeFromSuperview]; + } + std::unordered_set composition_order_set; for (int64_t view_id : composition_order_) { composition_order_set.insert(view_id); } - + // Remove unused platform views. for (int64_t view_id : active_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { - if (root_views_.find(view_id) == root_views_.end()) { - continue; - } - // We added a chain of super views to the platform view to handle clipping. - // The `platform_view_root` is the view at the top of the chain which is a direct subview of - // the `FlutterView`. UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; - [overlays_[view_id]->overlay_view.get() removeFromSuperview]; } } } @@ -458,56 +630,6 @@ views_to_dispose_.clear(); } -void FlutterPlatformViewsController::EnsureOverlayInitialized( - int64_t overlay_id, - std::shared_ptr ios_context, - GrContext* gr_context) { - FML_DCHECK(flutter_view_); - - auto overlay_it = overlays_.find(overlay_id); - - if (!gr_context) { - if (overlays_.count(overlay_id) != 0) { - return; - } - fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); - overlay_view.get().frame = flutter_view_.get().bounds; - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - overlays_[overlay_id] = std::make_unique( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - return; - } - - if (overlay_it != overlays_.end()) { - FlutterPlatformViewLayer* overlay = overlay_it->second.get(); - if (gr_context != overlay->gr_context) { - overlay->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = overlay_it->second->ios_surface.get(); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - overlay_it->second->surface = std::move(surface); - } - return; - } - auto contentsScale = flutter_view_.get().layer.contentsScale; - fml::scoped_nsobject overlay_view( - [[FlutterOverlayView alloc] initWithContentsScale:contentsScale]); - overlay_view.get().frame = flutter_view_.get().bounds; - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - overlays_[overlay_id] = std::make_unique( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - overlays_[overlay_id]->gr_context = gr_context; -} - } // namespace flutter // This recognizers delays touch events from being dispatched to the responder chain until it failed diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index d135d7d2ac290..59dc940c4d126 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #include "flutter/flow/embedded_views.h" +#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" @@ -68,12 +69,52 @@ struct FlutterPlatformViewLayer { std::unique_ptr ios_surface; std::unique_ptr surface; + // Whether a frame for this layer was submitted. + bool did_submit_last_frame; + // The GrContext that is currently used by the overlay surfaces. // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrContext* gr_context; }; +// This class isn't thread safe. +class FlutterPlatformViewLayerPool { + public: + FlutterPlatformViewLayerPool() = default; + ~FlutterPlatformViewLayerPool() = default; + + // Gets a layer from the pool if available, or allocates a new one. + // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. + std::shared_ptr GetLayer(GrContext* gr_context, + std::shared_ptr ios_context); + + // Gets the layers in the pool that aren't currently used. + // This method doesn't mark the layers as unused. + std::vector> GetUnusedLayers(); + + // Marks the layers in the pool as available for reuse. + void RecycleLayers(); + + private: + // The index of the entry in the layers_ vector that determines the beginning of the unused + // layers. For example, consider the following vector: + // _____ + // | 0 | + /// |---| + /// | 1 | <-- available_layer_index_ + /// |---| + /// | 2 | + /// |---| + /// + /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 + /// cannot be reused. + size_t available_layer_index_ = 0; + std::vector> layers_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); +}; + class FlutterPlatformViewsController { public: FlutterPlatformViewsController(); @@ -109,14 +150,37 @@ class FlutterPlatformViewsController { SkCanvas* CompositeEmbeddedView(int view_id); + // The rect of the platform view at index view_id. This rect has been translated into the + // host view coordinate system. Units are device screen pixels. + SkRect GetPlatformViewRect(int view_id); + // Discards all platform views instances and auxiliary resources. void Reset(); - bool SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context); + bool SubmitFrame(GrContext* gr_context, + std::shared_ptr ios_context, + SkCanvas* background_canvas); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: + static const size_t kMaxLayerAllocations = 2; + + using LayersMap = std::map>>; + + // The pool of reusable view layers. The pool allows to recycle layer in each frame. + std::unique_ptr layer_pool_; + + // The platform view's R-tree keyed off the view id, which contains any subsequent + // draw operation until the next platform view or the last leaf node in the layer tree. + // + // The R-trees are deleted by the FlutterPlatformViewsController.reset(). + std::map> platform_view_rtrees_; + + // The platform view's picture recorder keyed off the view id, which contains any subsequent + // operation until the next platform view or the end of the last leaf node in the layer tree. + std::map> picture_recorders_; + fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; fml::scoped_nsobject flutter_view_controller_; @@ -163,19 +227,12 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies; - std::map> picture_recorders_; - void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); - - void DetachUnusedLayers(); // Dispose the views in `views_to_dispose_`. void DisposeViews(); - void EnsureOverlayInitialized(int64_t overlay_id, - std::shared_ptr ios_context, - GrContext* gr_context); // This will return true after pre-roll if any of the embedded views // have mutated for last layer tree. @@ -215,6 +272,20 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from + // the picture on the layer's canvas. + std::shared_ptr GetLayer(GrContext* gr_context, + std::shared_ptr ios_context, + sk_sp picture, + SkRect rect, + int64_t view_id, + int64_t overlay_id); + // Removes overlay views and platform views that aren't needed in the current frame. + void RemoveUnusedLayers(); + // Appends the overlay views and platform view and sets their z index based on the composition + // order. + void BringLayersIntoView(LayersMap layer_map); + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index 9310fa1803f11..21db9530fe0bb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -20,7 +20,8 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() = default; +FlutterPlatformViewsController::FlutterPlatformViewsController() + : layer_pool_(std::make_unique()){}; FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 58233d8a2f656..8cba9285cfb02 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -77,7 +77,10 @@ class IOSSurface : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context) override; + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index fe85c77f12c2d..f51160e94e471 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -132,12 +132,18 @@ bool IsIosEmbeddedViewsPreviewEnabled() { } // |ExternalViewEmbedder| -bool IOSSurface::SubmitFrame(GrContext* context) { +bool IOSSurface::SubmitFrame(GrContext* context, SkCanvas* background_canvas) { TRACE_EVENT0("flutter", "IOSSurface::SubmitFrame"); FML_CHECK(platform_views_controller_ != nullptr); - bool submitted = platform_views_controller_->SubmitFrame(std::move(context), ios_context_); - [CATransaction commit]; + bool submitted = + platform_views_controller_->SubmitFrame(std::move(context), ios_context_, background_canvas); return submitted; } +// |ExternalViewEmbedder| +void IOSSurface::FinishFrame() { + TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + [CATransaction commit]; +} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 5e77073e7ff47..23658888f7caa 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -129,7 +129,8 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( } // |ExternalViewEmbedder| -bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { +bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -265,4 +266,7 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { return true; } +// |ExternalViewEmbedder| +void EmbedderExternalViewEmbedder::FinishFrame() {} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 7000d2cde04cd..63c944a88d7ef 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -89,7 +89,10 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context) override; + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index c24333a3a8a7f..818d902b3e2e9 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 3DEF491A23C3BE6500184216 /* golden_platform_view_transform_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */; }; 59A97FD8236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */; }; 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */; }; + 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */; }; 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */; }; @@ -149,6 +150,7 @@ 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone 8_simulator.png"; sourceTree = ""; }; 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_iPhone SE_simulator.png"; sourceTree = ""; }; 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png"; sourceTree = ""; }; + 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnobstructedPlatformViewTests.m; sourceTree = ""; }; 6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenPlatformViewTests.h; sourceTree = ""; }; 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenPlatformViewTests.m; sourceTree = ""; }; 6816DB9F2317573300A51400 /* GoldenImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenImage.h; sourceTree = ""; }; @@ -245,6 +247,7 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( + 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */, 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 3DE09E8B23C010BC006C9851 /* golden_platform_view_clippath_iPhone 8_simulator.png */, 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */, @@ -488,6 +491,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 348889b19b856..9bd732f647039 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -29,6 +29,13 @@ - (BOOL)application:(UIApplication*)application // the launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. NSDictionary* launchArgsMap = @{ @"--platform-view" : @"platform_view", + @"--platform-view-no-overlay-intersection" : @"platform_view_no_overlay_intersection", + @"--platform-view-two-intersecting-overlays" : @"platform_view_two_intersecting_overlays", + @"--platform-view-partial-intersection" : @"platform_view_partial_intersection", + @"--platform-view-one-overlay-two-intersecting-overlays" : + @"platform_view_one_overlay_two_intersecting_overlays", + @"--platform-view-multiple-without-overlays" : @"platform_view_multiple_without_overlays", + @"--platform-view-max-overlays" : @"platform_view_max_overlays", @"--platform-view-multiple" : @"platform_view_multiple", @"--platform-view-multiple-background-foreground" : @"platform_view_multiple_background_foreground", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m new file mode 100644 index 0000000000000..02e7eee35f098 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m @@ -0,0 +1,254 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface UnobstructedPlatformViewTests : XCTestCase + +@end + +@implementation UnobstructedPlatformViewTests + +- (void)setUp { + self.continueAfterFailure = NO; +} + +// A is the layer, which z index is higher than the platform view. +// +--------+ +// | PV | +---+ +// +--------+ | A | +// +---+ +- (void)testNoOverlay { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-no-overlay-intersection" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertFalse(overlay.exists); +} + +// A is the layer above the platform view. +// +-----------------+ +// | PV +---+ | +// | | A | | +// | +---+ | +// +-----------------+ +- (void)testOneOverlay { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 150); + XCTAssertEqual(overlay.frame.origin.y, 150); + XCTAssertEqual(overlay.frame.size.width, 50); + XCTAssertEqual(overlay.frame.size.height, 50); +} + +// A is the layer above the platform view. +// +-----------------+ +// | PV +---+ | +// +-----------| A |-+ +// +---+ +- (void)testOneOverlayPartialIntersection { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-partial-intersection" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 200); + XCTAssertEqual(overlay.frame.origin.y, 250); + XCTAssertEqual(overlay.frame.size.width, 50); + // Half the height of the overlay. + XCTAssertEqual(overlay.frame.size.height, 25); +} + +// A and B are the layers above the platform view. +// +--------------------+ +// | PV +------------+ | +// | | B +-----+ | | +// | +---| A |-+ | +// +----------| |---+ +// +-----+ +- (void)testTwoIntersectingOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-two-intersecting-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 150); + XCTAssertEqual(overlay.frame.origin.y, 150); + XCTAssertEqual(overlay.frame.size.width, 75); + XCTAssertEqual(overlay.frame.size.height, 75); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); +} + +// A, B, and C are the layers above the platform view. +// +-------------------------+ +// | PV +-----------+ | +// | +---+ | B +-----+ | | +// | | C | +---| A |-+ | +// | +---+ +-----+ | +// +-------------------------+ +- (void)testOneOverlayAndTwoIntersectingOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-one-overlay-two-intersecting-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay1.exists); + XCTAssertEqual(overlay1.frame.origin.x, 150); + XCTAssertEqual(overlay1.frame.origin.y, 150); + XCTAssertEqual(overlay1.frame.size.width, 75); + XCTAssertEqual(overlay1.frame.size.height, 75); + + XCUIElement* overlay2 = app.otherElements[@"platform_view[0].overlay[1]"]; + XCTAssertTrue(overlay2.exists); + XCTAssertEqual(overlay2.frame.origin.x, 75); + XCTAssertEqual(overlay2.frame.origin.y, 225); + XCTAssertEqual(overlay2.frame.size.width, 50); + XCTAssertEqual(overlay2.frame.size.height, 50); +} + +// A is the layer, which z index is higher than the platform view. +// +--------+ +// | PV | +---+ +// +--------+ | A | +// +--------+ +---+ +// | PV | +// +--------+ +- (void)testMultiplePlatformViewsWithoutOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-multiple-without-overlays" ]; + [app launch]; + + XCUIElement* platform_view1 = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view1.exists); + XCTAssertEqual(platform_view1.frame.origin.x, 25); + XCTAssertEqual(platform_view1.frame.origin.y, 325); + XCTAssertEqual(platform_view1.frame.size.width, 250); + XCTAssertEqual(platform_view1.frame.size.height, 250); + + XCUIElement* platform_view2 = app.textViews[@"platform_view[1]"]; + XCTAssertTrue(platform_view2.exists); + XCTAssertEqual(platform_view2.frame.origin.x, 25); + XCTAssertEqual(platform_view2.frame.origin.y, 25); + XCTAssertEqual(platform_view2.frame.size.width, 250); + XCTAssertEqual(platform_view2.frame.size.height, 250); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[0]"].exists); + XCTAssertFalse(app.otherElements[@"platform_view[1].overlay[0]"].exists); +} + +// A is the layer above both platform view. +// +------------+ +// | PV +----+ | +// +-----| A |-+ +// +-----| |-+ +// | PV +----+ | +// +------------+ +- (void)testMultiplePlatformViewsWithOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-multiple-background-foreground" ]; + [app launch]; + + XCUIElement* platform_view1 = app.textViews[@"platform_view[8]"]; + XCTAssertTrue(platform_view1.exists); + XCTAssertEqual(platform_view1.frame.origin.x, 25); + XCTAssertEqual(platform_view1.frame.origin.y, 325); + XCTAssertEqual(platform_view1.frame.size.width, 250); + XCTAssertEqual(platform_view1.frame.size.height, 250); + + XCUIElement* platform_view2 = app.textViews[@"platform_view[9]"]; + XCTAssertTrue(platform_view2.exists); + XCTAssertEqual(platform_view2.frame.origin.x, 25); + XCTAssertEqual(platform_view2.frame.origin.y, 25); + XCTAssertEqual(platform_view2.frame.size.width, 250); + XCTAssertEqual(platform_view2.frame.size.height, 250); + + XCUIElement* overlay1 = app.otherElements[@"platform_view[8].overlay[0]"]; + XCTAssertTrue(overlay1.exists); + XCTAssertEqual(overlay1.frame.origin.x, 25); + XCTAssertEqual(overlay1.frame.origin.y, 325); + XCTAssertEqual(overlay1.frame.size.width, 225); + XCTAssertEqual(overlay1.frame.size.height, 175); + + XCUIElement* overlay2 = app.otherElements[@"platform_view[9].overlay[0]"]; + XCTAssertTrue(overlay2.exists); + XCTAssertEqual(overlay2.frame.origin.x, 25); + XCTAssertEqual(overlay2.frame.origin.y, 25); + XCTAssertEqual(overlay2.frame.size.width, 225); + XCTAssertEqual(overlay2.frame.size.height, 250); +} + +// More then two overlays are merged into a single layer. +// +---------------------+ +// | +---+ +---+ +---+ | +// | | A | | B | | C | | +// | +---+ +---+ +---+ | +// | +-------+ | +// +-| D |-----------+ +// +-------+ +- (void)testPlatformViewsMaxOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-max-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 75); + XCTAssertEqual(overlay.frame.origin.y, 85); + XCTAssertEqual(overlay.frame.size.width, 150); + XCTAssertEqual(overlay.frame.size.height, 190); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png index a193faeb040223e56343903c777f9181edb5bcc0..9ec19ab474f03b6cc7786cb038ee3b3e6d0c51a4 100644 GIT binary patch literal 20295 zcmeHuX*iVa8@HyJnz3Yzr3E$ieMzzvCQH_kA+oDbk8B}3O<5*oDSL~pkUjg(q>u%s=030UI)CT+JD2M|+}G1lqoY1XO+`gTr=hN* zPelcrrJ_PeAz|PX)smMN!DXMTzM2wMUK`sa_>ZTJv4-u13si#O9!Z7RhoRaJJp%qx z?K?(=pxjeYY3^hF_g;S=|DR{Tnjtr+;D4Sm0axfxB=`Z+f3C1(*xx5$v&sAa-Xo-- zN15f^xWEPFtbW;*ii(8?`q`(U&pi)5WV@lFYXGj$#-Yno@DKm)6}p!srTj#Pg6l&K zm2(E3`=@BVmW3Qoo zEAE=V()MiS$ENStBiA{d&TI4$Uu9`u;g8Qjx^L@!$Qxhsl75%>84gx2Zkt9p zRMxIl)orxYi*C=;2XrpKI37oy58lCFSM-=&@xF=oTnrW3#;5$UiQAH?R`gyXGY?cu zrq>A6T!x{2ge|-jD(&p3OzZK`!Cx68%Q=PKgb{}sqw2|M$ zql9LA7VneSbIERwo4HO~Pv=4E+IARkA+J5RTrU5ixIMDd-Gj|3_n70e{kVRPqm%YbIrEf=)%WS0xw@4K>=zfOA>tKgx*wpiLK}#s8`F)Z8tF7a!t7z7<4>CBITKz z!{_m5FWzUiD$~o$dd}A|mHK|YyyH4I(Bw_OI$M<_R7_9vIen|xbfYUnVx`aZZ%(5}4Y;&(4*>&L_jytIj{Ezty%+*$Wvj~__PCB!-R41mQZ@4VB z(*JF1Rc{KP5FV~<_snHf?8gPCb<3b_3%5`07E%0D?E@JS){IU*z3tSj^fc0a9lab$ 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 literal 30022 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ΠAr29j`n?8;-QZE z4jSFDu$|OBK+1@&kY%gDyh4f+et*UW!xwP*gQNm<3K_Yi(Xv2`G0u{G@CDL_`Za|R z0&ba{A^qz!h73(?gBQ zaOaF%@BB*^QHtUx^a&4#swelbeQ@;feaTS45;*zevVw|@B_*Du_fg$vTTApLhT z;(~N8NauodE=cEg)wx}DZdaZ22H-9H%#@2te_y_G)xmx1@#nP+_vs7wCD1Qx;69A{ z-@X&;4$L>JfxjakBUrV-?pPC7bYAj`F8b4z{e%L^L5)Ap&Ej}pv9OANp0FJk6lCu2 z`qwMCQ-p#C$T2zwwupZl2?j`8#a1jqb& z0=E@=#V?uu=M`PWHYtj}0{Y_#&Wfu+&iGHHKM&yc@?V(~vi|c9FG*CCezoXdPx+n% zIcrwd|B-1q@2k?|%s;R2x|fU6rSJXgF(6k7@Me>%1g;WLALUvCG}*aI;3|QWvbdJO zwFIsuaDJV@wFIsua4msbJ8%mq_&GOM30x(hZ4Is^K;6hy0#^x~-^Fq*;eTWaOX`;t XeZBM9{0{PeD?M`X*ny1wR>A)dG2_6W diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png index 793082f8f0f7c4edaa29fd14c5ee5a782d1e69b1..a2e9351d385e0b2f7800ec71448a05e2736e196e 100644 GIT binary patch literal 22360 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 mf=d=$vf%&Cf@1cqGyR|38zWqd;cH7-*4ss literal 33727 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%0D$f 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 HeDr?+Nh376 diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 494d6585aa7fe..0ab3f7c5352f1 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -19,6 +19,12 @@ import 'src/touches_scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1), 'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2), 'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3), diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 0dde98fd9eebb..fcea41a65f7d5 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -48,6 +48,224 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin } } +/// A simple platform view with overlay that doesn't intersect with the platform view. +class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewNoOverlayIntersectionScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + finishBuilderByAddingPlatformViewAndPicture( + builder, + 0, + overlayOffset: const Offset(150, 350), + ); + } +} + +/// A simple platform view with an overlay that partially intersects with the platform view. +class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewPartialIntersectionScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + finishBuilderByAddingPlatformViewAndPicture( + builder, + 0, + overlayOffset: const Offset(150, 250), + ); + } +} + +/// A simple platform view with two overlays that intersect with each other and the platform view. +class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// A simple platform view with one overlay and two overlays that intersect with each other and the platform view. +class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewOneOverlayTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, 200), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// Two platform views without an overlay intersecting either platform view. +class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + MultiPlatformViewWithoutOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + builder.pushOffset(0, 600); + _addPlatformViewtoScene(builder, 0, 500, 500); + builder.pop(); + + _addPlatformViewtoScene(builder, 1, 500, 500); + + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawRect( + const Rect.fromLTRB(0, 0, 100, 1000), + Paint()..color = const Color(0xFFFF0000), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(580, 0), picture); + + builder.pop(); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// A simple platform view with too many overlays result in a single native view. +class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewMaxOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, 200), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, -80), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + /// Builds a scene with 2 platform views. class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. @@ -424,12 +642,17 @@ mixin _BasePlatformViewScenarioMixin on Scenario { } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { + void finishBuilderByAddingPlatformViewAndPicture( + SceneBuilder sceneBuilder, + int viewId, { + Offset overlayOffset, + }) { + overlayOffset ??= const Offset(50, 50); _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( - const Offset(50, 50), + overlayOffset, 50, Paint()..color = const Color(0xFFABCDEF), ); From bba1a3cca86f1dd9f500112a0cd87262b4d4e09a Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 20 Mar 2020 13:31:01 -0700 Subject: [PATCH 064/386] Roll buildroot to a0fb98af (#17229) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f515a615d4e1b..02e094c7662ba 100644 --- a/DEPS +++ b/DEPS @@ -137,7 +137,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '4489a78bf94bb9d8ea6a127106f85b7710d57f70', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', # Fuchsia compatibility # From 70f6d18bfa449ef221efe2b18e48874ba34dc417 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 20 Mar 2020 14:46:09 -0700 Subject: [PATCH 065/386] Revert "Implement unobstructed Platform Views on iOS (#17049)" (#17233) This reverts commit 2627634be807a28f4d56c5d7cd878c3ff6ce07ac. --- flow/embedded_views.cc | 5 +- flow/embedded_views.h | 5 +- flow/layers/picture_layer.cc | 2 +- flow/layers/picture_layer_unittests.cc | 3 + shell/common/rasterizer.cc | 12 +- .../framework/Source/FlutterPlatformViews.mm | 320 ++++++------------ .../Source/FlutterPlatformViews_Internal.h | 87 +---- .../Source/FlutterPlatformViews_Internal.mm | 3 +- shell/platform/darwin/ios/ios_surface.h | 5 +- shell/platform/darwin/ios/ios_surface.mm | 12 +- .../embedder_external_view_embedder.cc | 6 +- .../embedder_external_view_embedder.h | 5 +- .../Scenarios.xcodeproj/project.pbxproj | 4 - .../ios/Scenarios/Scenarios/AppDelegate.m | 7 - .../UnobstructedPlatformViewTests.m | 254 -------------- ...tform_view_clippath_iPhone 8_simulator.png | Bin 20295 -> 30022 bytes ...form_view_transform_iPhone 8_simulator.png | Bin 22360 -> 33727 bytes testing/scenario_app/lib/main.dart | 6 - .../scenario_app/lib/src/platform_view.dart | 227 +------------ 19 files changed, 124 insertions(+), 839 deletions(-) delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 5234bf1e50c8c..c660f4691318b 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -6,13 +6,10 @@ namespace flutter { -bool ExternalViewEmbedder::SubmitFrame(GrContext* context, - SkCanvas* background_canvas) { +bool ExternalViewEmbedder::SubmitFrame(GrContext* context) { return false; }; -void ExternalViewEmbedder::FinishFrame(){}; - void MutatorsStack::PushClipRect(const SkRect& rect) { std::shared_ptr element = std::make_shared(rect); vector_.push_back(element); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 7a491a8a152ef..030eb88c8a06d 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -248,10 +248,7 @@ class ExternalViewEmbedder { // Must be called on the UI thread. virtual SkCanvas* CompositeEmbeddedView(int view_id) = 0; - virtual bool SubmitFrame(GrContext* context, SkCanvas* background_canvas); - - // This is called after submitting the embedder frame and the surface frame. - virtual void FinishFrame(); + virtual bool SubmitFrame(GrContext* context); FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 08c09cc9e833b..3bc7e394c1033 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -59,7 +59,7 @@ void PictureLayer::Paint(PaintContext& context) const { return; } } - picture()->playback(context.leaf_nodes_canvas); + context.leaf_nodes_canvas->drawPicture(picture()); } } // namespace flutter diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 4f565cf500ecc..687c870eeac66 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -94,6 +94,9 @@ TEST_F(PictureLayerTest, SimplePicture) { 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( layer_offset_matrix)}}, #endif + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), + SkMatrix()}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f7e4350fe9c97..811898b5a42d2 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -342,17 +342,9 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { if (raster_status == RasterStatus::kFailed) { return raster_status; } + frame->Submit(); if (external_view_embedder != nullptr) { - external_view_embedder->SubmitFrame(surface_->GetContext(), - root_surface_canvas); - // The external view embedder may mutate the root surface canvas while - // submitting the frame. - // Therefore, submit the final frame after asking the external view - // embedder to submit the frame. - frame->Submit(); - external_view_embedder->FinishFrame(); - } else { - frame->Submit(); + external_view_embedder->SubmitFrame(surface_->GetContext()); } FireNextFrameCallbackIfPresent(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b96fcbbbaa76a..b86c4623fa7a7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -8,77 +8,16 @@ #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/ios_surface_gl.h" -#include #include #include #include #include "FlutterPlatformViews_Internal.h" -#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" namespace flutter { -std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( - GrContext* gr_context, - std::shared_ptr ios_context) { - if (available_layer_index_ >= layers_.size()) { - std::shared_ptr layer; - - if (!gr_context) { - fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - - layer = std::make_shared( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - } else { - CGFloat screenScale = [UIScreen mainScreen].scale; - fml::scoped_nsobject overlay_view( - [[FlutterOverlayView alloc] initWithContentsScale:screenScale]); - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - - layer = std::make_shared( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - layer->gr_context = gr_context; - } - layers_.push_back(layer); - } - auto layer = layers_[available_layer_index_]; - if (gr_context != layer->gr_context) { - layer->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = layer->ios_surface.get(); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - layer->surface = std::move(surface); - } - available_layer_index_++; - return layer; -} - -void FlutterPlatformViewLayerPool::RecycleLayers() { - available_layer_index_ = 0; -} - -std::vector> -FlutterPlatformViewLayerPool::GetUnusedLayers() { - std::vector> results; - for (size_t i = available_layer_index_; i < layers_.size(); i++) { - results.push_back(layers_[i]); - } - return results; -} - void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { flutter_view_.reset([flutter_view retain]); } @@ -144,9 +83,6 @@ NSObject* embedded_view = [factory createWithFrame:CGRectZero viewIdentifier:viewId arguments:params]; - // Set a unique view identifier, so the platform view can be identified in unit tests. - [embedded_view view].accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%ld]", viewId]; views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] @@ -260,11 +196,8 @@ int view_id, std::unique_ptr params) { picture_recorders_[view_id] = std::make_unique(); - - auto rtree_factory = RTreeFactory(); - platform_view_rtrees_[view_id] = rtree_factory.getInstance(); - picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_), &rtree_factory); - + picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_)); + picture_recorders_[view_id]->getRecordingCanvas()->clear(SK_ColorTRANSPARENT); composition_order_.push_back(view_id); if (current_composition_params_.count(view_id) == 1 && @@ -428,182 +361,77 @@ composition_order_.clear(); active_composition_order_.clear(); picture_recorders_.clear(); - platform_view_rtrees_.clear(); current_composition_params_.clear(); clip_count_.clear(); views_to_recomposite_.clear(); - layer_pool_->RecycleLayers(); -} - -SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) { - UIView* platform_view = [views_[view_id].get() view]; - UIScreen* screen = [UIScreen mainScreen]; - CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds - toView:flutter_view_]; - return SkRect::MakeXYWH(platform_view_cgrect.origin.x * screen.scale, // - platform_view_cgrect.origin.y * screen.scale, // - platform_view_cgrect.size.width * screen.scale, // - platform_view_cgrect.size.height * screen.scale // - ); } bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context, - SkCanvas* background_canvas) { + std::shared_ptr ios_context) { DisposeViews(); - // Clipping the background canvas before drawing the picture recorders requires to - // save and restore the clip context. - SkAutoCanvasRestore save(background_canvas, /*doSave=*/true); - // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. - LayersMap platform_view_layers; - - auto did_submit = true; - auto num_platform_views = composition_order_.size(); - - for (size_t i = 0; i < num_platform_views; i++) { - int64_t platform_view_id = composition_order_[i]; - sk_sp rtree = platform_view_rtrees_[platform_view_id]; - sk_sp picture = picture_recorders_[platform_view_id]->finishRecordingAsPicture(); - - // Check if the current picture contains overlays that intersect with the - // current platform view or any of the previous platform views. - for (size_t j = i + 1; j > 0; j--) { - int64_t current_platform_view_id = composition_order_[j - 1]; - SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); - std::list intersection_rects = - rtree->searchNonOverlappingDrawnRects(platform_view_rect); - auto allocation_size = intersection_rects.size(); - - // For testing purposes, the overlay id is used to find the overlay view. - // This is the index of the layer for the current platform view. - auto overlay_id = platform_view_layers[current_platform_view_id].size(); - - // If the max number of allocations per platform view is exceeded, - // then join all the rects into a single one. - // - // TODO(egarciad): Consider making this configurable. - // https://github.com/flutter/flutter/issues/52510 - if (allocation_size > kMaxLayerAllocations) { - SkRect joined_rect; - for (const SkRect& rect : intersection_rects) { - joined_rect.join(rect); - } - // Replace the rects in the intersection rects list for a single rect that is - // the union of all the rects in the list. - intersection_rects.clear(); - intersection_rects.push_back(joined_rect); - } - for (SkRect& joined_rect : intersection_rects) { - // Get the intersection rect between the current rect - // and the platform view rect. - joined_rect.intersect(platform_view_rect); - // Clip the background canvas, so it doesn't contain any of the pixels drawn - // on the overlay layer. - background_canvas->clipRect(joined_rect, SkClipOp::kDifference); - // Get a new host layer. - auto layer = GetLayer(gr_context, // - ios_context, // - picture, // - joined_rect, // - current_platform_view_id, // - overlay_id // - ); - did_submit &= layer->did_submit_last_frame; - platform_view_layers[current_platform_view_id].push_back(layer); - overlay_id++; - } - } - background_canvas->drawPicture(picture); - } - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(); - // Organize the layers by their z indexes. - BringLayersIntoView(platform_view_layers); - // Mark all layers as available, so they can be used in the next frame. - layer_pool_->RecycleLayers(); - // Reset the composition order, so next frame starts empty. - composition_order_.clear(); - return did_submit; -} - -void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { + bool did_submit = true; + for (int64_t view_id : composition_order_) { + EnsureOverlayInitialized(view_id, ios_context, gr_context); + auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); + // If frame is null, AcquireFrame already printed out an error message. + if (frame) { + SkCanvas* canvas = frame->SkiaCanvas(); + canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); + canvas->flush(); + did_submit &= frame->Submit(); + } + } + picture_recorders_.clear(); + if (composition_order_ == active_composition_order_) { + composition_order_.clear(); + return did_submit; + } + DetachUnusedLayers(); + active_composition_order_.clear(); UIView* flutter_view = flutter_view_.get(); - auto zIndex = 0; - for (size_t i = 0; i < composition_order_.size(); i++) { - int64_t platform_view_id = composition_order_[i]; - std::vector> layers = layer_map[platform_view_id]; - UIView* platform_view_root = root_views_[platform_view_id].get(); - if (platform_view_root.superview != flutter_view) { - [flutter_view addSubview:platform_view_root]; + for (size_t i = 0; i < composition_order_.size(); i++) { + int view_id = composition_order_[i]; + // We added a chain of super views to the platform view to handle clipping. + // The `platform_view_root` is the view at the top of the chain which is a direct subview of the + // `FlutterView`. + UIView* platform_view_root = root_views_[view_id].get(); + UIView* overlay = overlays_[view_id]->overlay_view; + FML_CHECK(platform_view_root.superview == overlay.superview); + if (platform_view_root.superview == flutter_view) { + [flutter_view bringSubviewToFront:platform_view_root]; + [flutter_view bringSubviewToFront:overlay]; } else { - platform_view_root.layer.zPosition = zIndex++; - } - for (const std::shared_ptr& layer : layers) { - if ([layer->overlay_view superview] != flutter_view) { - [flutter_view addSubview:layer->overlay_view]; - } else { - layer->overlay_view.get().layer.zPosition = zIndex++; - } + [flutter_view addSubview:platform_view_root]; + [flutter_view addSubview:overlay]; + overlay.frame = flutter_view.bounds; } - active_composition_order_.push_back(platform_view_id); - } -} -std::shared_ptr FlutterPlatformViewsController::GetLayer( - GrContext* gr_context, - std::shared_ptr ios_context, - sk_sp picture, - SkRect rect, - int64_t view_id, - int64_t overlay_id) { - auto layer = layer_pool_->GetLayer(gr_context, ios_context); - auto screenScale = [UIScreen mainScreen].scale; - // Set the size of the overlay UIView. - layer->overlay_view.get().frame = CGRectMake(rect.x() / screenScale, // - rect.y() / screenScale, // - rect.width() / screenScale, // - rect.height() / screenScale // - ); - // Set a unique view identifier, so the overlay can be identified in unit tests. - layer->overlay_view.get().accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; - - std::unique_ptr frame = - layer->surface->AcquireFrame(SkISize::Make(rect.width(), rect.height())); - // If frame is null, AcquireFrame already printed out an error message. - if (!frame) { - return layer; - } - auto overlay_canvas = frame->SkiaCanvas(); - overlay_canvas->clear(SK_ColorTRANSPARENT); - // Offset the picture since its absolute position on the scene is determined - // by the position of the overlay view. - overlay_canvas->translate(-rect.x(), -rect.y()); - overlay_canvas->drawPicture(picture); - - layer->did_submit_last_frame = frame->Submit(); - return layer; -} - -void FlutterPlatformViewsController::RemoveUnusedLayers() { - auto layers = layer_pool_->GetUnusedLayers(); - for (const std::shared_ptr& layer : layers) { - [layer->overlay_view removeFromSuperview]; + active_composition_order_.push_back(view_id); } + composition_order_.clear(); + return did_submit; +} +void FlutterPlatformViewsController::DetachUnusedLayers() { std::unordered_set composition_order_set; for (int64_t view_id : composition_order_) { composition_order_set.insert(view_id); } - // Remove unused platform views. + for (int64_t view_id : active_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { + if (root_views_.find(view_id) == root_views_.end()) { + continue; + } + // We added a chain of super views to the platform view to handle clipping. + // The `platform_view_root` is the view at the top of the chain which is a direct subview of + // the `FlutterView`. UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; + [overlays_[view_id]->overlay_view.get() removeFromSuperview]; } } } @@ -630,6 +458,56 @@ views_to_dispose_.clear(); } +void FlutterPlatformViewsController::EnsureOverlayInitialized( + int64_t overlay_id, + std::shared_ptr ios_context, + GrContext* gr_context) { + FML_DCHECK(flutter_view_); + + auto overlay_it = overlays_.find(overlay_id); + + if (!gr_context) { + if (overlays_.count(overlay_id) != 0) { + return; + } + fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); + overlay_view.get().frame = flutter_view_.get().bounds; + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + overlays_[overlay_id] = std::make_unique( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + return; + } + + if (overlay_it != overlays_.end()) { + FlutterPlatformViewLayer* overlay = overlay_it->second.get(); + if (gr_context != overlay->gr_context) { + overlay->gr_context = gr_context; + // The overlay already exists, but the GrContext was changed so we need to recreate + // the rendering surface with the new GrContext. + IOSSurface* ios_surface = overlay_it->second->ios_surface.get(); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + overlay_it->second->surface = std::move(surface); + } + return; + } + auto contentsScale = flutter_view_.get().layer.contentsScale; + fml::scoped_nsobject overlay_view( + [[FlutterOverlayView alloc] initWithContentsScale:contentsScale]); + overlay_view.get().frame = flutter_view_.get().bounds; + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + overlays_[overlay_id] = std::make_unique( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + overlays_[overlay_id]->gr_context = gr_context; +} + } // namespace flutter // This recognizers delays touch events from being dispatched to the responder chain until it failed diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 59dc940c4d126..d135d7d2ac290 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,7 +6,6 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #include "flutter/flow/embedded_views.h" -#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" @@ -69,52 +68,12 @@ struct FlutterPlatformViewLayer { std::unique_ptr ios_surface; std::unique_ptr surface; - // Whether a frame for this layer was submitted. - bool did_submit_last_frame; - // The GrContext that is currently used by the overlay surfaces. // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrContext* gr_context; }; -// This class isn't thread safe. -class FlutterPlatformViewLayerPool { - public: - FlutterPlatformViewLayerPool() = default; - ~FlutterPlatformViewLayerPool() = default; - - // Gets a layer from the pool if available, or allocates a new one. - // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. - std::shared_ptr GetLayer(GrContext* gr_context, - std::shared_ptr ios_context); - - // Gets the layers in the pool that aren't currently used. - // This method doesn't mark the layers as unused. - std::vector> GetUnusedLayers(); - - // Marks the layers in the pool as available for reuse. - void RecycleLayers(); - - private: - // The index of the entry in the layers_ vector that determines the beginning of the unused - // layers. For example, consider the following vector: - // _____ - // | 0 | - /// |---| - /// | 1 | <-- available_layer_index_ - /// |---| - /// | 2 | - /// |---| - /// - /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 - /// cannot be reused. - size_t available_layer_index_ = 0; - std::vector> layers_; - - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); -}; - class FlutterPlatformViewsController { public: FlutterPlatformViewsController(); @@ -150,37 +109,14 @@ class FlutterPlatformViewsController { SkCanvas* CompositeEmbeddedView(int view_id); - // The rect of the platform view at index view_id. This rect has been translated into the - // host view coordinate system. Units are device screen pixels. - SkRect GetPlatformViewRect(int view_id); - // Discards all platform views instances and auxiliary resources. void Reset(); - bool SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context, - SkCanvas* background_canvas); + bool SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: - static const size_t kMaxLayerAllocations = 2; - - using LayersMap = std::map>>; - - // The pool of reusable view layers. The pool allows to recycle layer in each frame. - std::unique_ptr layer_pool_; - - // The platform view's R-tree keyed off the view id, which contains any subsequent - // draw operation until the next platform view or the last leaf node in the layer tree. - // - // The R-trees are deleted by the FlutterPlatformViewsController.reset(). - std::map> platform_view_rtrees_; - - // The platform view's picture recorder keyed off the view id, which contains any subsequent - // operation until the next platform view or the end of the last leaf node in the layer tree. - std::map> picture_recorders_; - fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; fml::scoped_nsobject flutter_view_controller_; @@ -227,12 +163,19 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies; + std::map> picture_recorders_; + void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); + + void DetachUnusedLayers(); // Dispose the views in `views_to_dispose_`. void DisposeViews(); + void EnsureOverlayInitialized(int64_t overlay_id, + std::shared_ptr ios_context, + GrContext* gr_context); // This will return true after pre-roll if any of the embedded views // have mutated for last layer tree. @@ -272,20 +215,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from - // the picture on the layer's canvas. - std::shared_ptr GetLayer(GrContext* gr_context, - std::shared_ptr ios_context, - sk_sp picture, - SkRect rect, - int64_t view_id, - int64_t overlay_id); - // Removes overlay views and platform views that aren't needed in the current frame. - void RemoveUnusedLayers(); - // Appends the overlay views and platform view and sets their z index based on the composition - // order. - void BringLayersIntoView(LayersMap layer_map); - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index 21db9530fe0bb..9310fa1803f11 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -20,8 +20,7 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() - : layer_pool_(std::make_unique()){}; +FlutterPlatformViewsController::FlutterPlatformViewsController() = default; FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 8cba9285cfb02..58233d8a2f656 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -77,10 +77,7 @@ class IOSSurface : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; - - // |ExternalViewEmbedder| - void FinishFrame() override; + bool SubmitFrame(GrContext* context) override; public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index f51160e94e471..fe85c77f12c2d 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -132,18 +132,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { } // |ExternalViewEmbedder| -bool IOSSurface::SubmitFrame(GrContext* context, SkCanvas* background_canvas) { +bool IOSSurface::SubmitFrame(GrContext* context) { TRACE_EVENT0("flutter", "IOSSurface::SubmitFrame"); FML_CHECK(platform_views_controller_ != nullptr); - bool submitted = - platform_views_controller_->SubmitFrame(std::move(context), ios_context_, background_canvas); - return submitted; -} - -// |ExternalViewEmbedder| -void IOSSurface::FinishFrame() { - TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + bool submitted = platform_views_controller_->SubmitFrame(std::move(context), ios_context_); [CATransaction commit]; + return submitted; } } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 23658888f7caa..5e77073e7ff47 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -129,8 +129,7 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( } // |ExternalViewEmbedder| -bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, - SkCanvas* background_canvas) { +bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -266,7 +265,4 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, return true; } -// |ExternalViewEmbedder| -void EmbedderExternalViewEmbedder::FinishFrame() {} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 63c944a88d7ef..7000d2cde04cd 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -89,10 +89,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; - - // |ExternalViewEmbedder| - void FinishFrame() override; + bool SubmitFrame(GrContext* context) override; // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 818d902b3e2e9..c24333a3a8a7f 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ 3DEF491A23C3BE6500184216 /* golden_platform_view_transform_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */; }; 59A97FD8236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */; }; 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */; }; - 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */; }; 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */; }; @@ -150,7 +149,6 @@ 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone 8_simulator.png"; sourceTree = ""; }; 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_iPhone SE_simulator.png"; sourceTree = ""; }; 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png"; sourceTree = ""; }; - 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnobstructedPlatformViewTests.m; sourceTree = ""; }; 6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenPlatformViewTests.h; sourceTree = ""; }; 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenPlatformViewTests.m; sourceTree = ""; }; 6816DB9F2317573300A51400 /* GoldenImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenImage.h; sourceTree = ""; }; @@ -247,7 +245,6 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( - 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */, 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 3DE09E8B23C010BC006C9851 /* golden_platform_view_clippath_iPhone 8_simulator.png */, 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */, @@ -491,7 +488,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 9bd732f647039..348889b19b856 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -29,13 +29,6 @@ - (BOOL)application:(UIApplication*)application // the launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. NSDictionary* launchArgsMap = @{ @"--platform-view" : @"platform_view", - @"--platform-view-no-overlay-intersection" : @"platform_view_no_overlay_intersection", - @"--platform-view-two-intersecting-overlays" : @"platform_view_two_intersecting_overlays", - @"--platform-view-partial-intersection" : @"platform_view_partial_intersection", - @"--platform-view-one-overlay-two-intersecting-overlays" : - @"platform_view_one_overlay_two_intersecting_overlays", - @"--platform-view-multiple-without-overlays" : @"platform_view_multiple_without_overlays", - @"--platform-view-max-overlays" : @"platform_view_max_overlays", @"--platform-view-multiple" : @"platform_view_multiple", @"--platform-view-multiple-background-foreground" : @"platform_view_multiple_background_foreground", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m deleted file mode 100644 index 02e7eee35f098..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface UnobstructedPlatformViewTests : XCTestCase - -@end - -@implementation UnobstructedPlatformViewTests - -- (void)setUp { - self.continueAfterFailure = NO; -} - -// A is the layer, which z index is higher than the platform view. -// +--------+ -// | PV | +---+ -// +--------+ | A | -// +---+ -- (void)testNoOverlay { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-no-overlay-intersection" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertFalse(overlay.exists); -} - -// A is the layer above the platform view. -// +-----------------+ -// | PV +---+ | -// | | A | | -// | +---+ | -// +-----------------+ -- (void)testOneOverlay { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 150); - XCTAssertEqual(overlay.frame.origin.y, 150); - XCTAssertEqual(overlay.frame.size.width, 50); - XCTAssertEqual(overlay.frame.size.height, 50); -} - -// A is the layer above the platform view. -// +-----------------+ -// | PV +---+ | -// +-----------| A |-+ -// +---+ -- (void)testOneOverlayPartialIntersection { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-partial-intersection" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 200); - XCTAssertEqual(overlay.frame.origin.y, 250); - XCTAssertEqual(overlay.frame.size.width, 50); - // Half the height of the overlay. - XCTAssertEqual(overlay.frame.size.height, 25); -} - -// A and B are the layers above the platform view. -// +--------------------+ -// | PV +------------+ | -// | | B +-----+ | | -// | +---| A |-+ | -// +----------| |---+ -// +-----+ -- (void)testTwoIntersectingOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-two-intersecting-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 150); - XCTAssertEqual(overlay.frame.origin.y, 150); - XCTAssertEqual(overlay.frame.size.width, 75); - XCTAssertEqual(overlay.frame.size.height, 75); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); -} - -// A, B, and C are the layers above the platform view. -// +-------------------------+ -// | PV +-----------+ | -// | +---+ | B +-----+ | | -// | | C | +---| A |-+ | -// | +---+ +-----+ | -// +-------------------------+ -- (void)testOneOverlayAndTwoIntersectingOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-one-overlay-two-intersecting-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay1.exists); - XCTAssertEqual(overlay1.frame.origin.x, 150); - XCTAssertEqual(overlay1.frame.origin.y, 150); - XCTAssertEqual(overlay1.frame.size.width, 75); - XCTAssertEqual(overlay1.frame.size.height, 75); - - XCUIElement* overlay2 = app.otherElements[@"platform_view[0].overlay[1]"]; - XCTAssertTrue(overlay2.exists); - XCTAssertEqual(overlay2.frame.origin.x, 75); - XCTAssertEqual(overlay2.frame.origin.y, 225); - XCTAssertEqual(overlay2.frame.size.width, 50); - XCTAssertEqual(overlay2.frame.size.height, 50); -} - -// A is the layer, which z index is higher than the platform view. -// +--------+ -// | PV | +---+ -// +--------+ | A | -// +--------+ +---+ -// | PV | -// +--------+ -- (void)testMultiplePlatformViewsWithoutOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-multiple-without-overlays" ]; - [app launch]; - - XCUIElement* platform_view1 = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view1.exists); - XCTAssertEqual(platform_view1.frame.origin.x, 25); - XCTAssertEqual(platform_view1.frame.origin.y, 325); - XCTAssertEqual(platform_view1.frame.size.width, 250); - XCTAssertEqual(platform_view1.frame.size.height, 250); - - XCUIElement* platform_view2 = app.textViews[@"platform_view[1]"]; - XCTAssertTrue(platform_view2.exists); - XCTAssertEqual(platform_view2.frame.origin.x, 25); - XCTAssertEqual(platform_view2.frame.origin.y, 25); - XCTAssertEqual(platform_view2.frame.size.width, 250); - XCTAssertEqual(platform_view2.frame.size.height, 250); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[0]"].exists); - XCTAssertFalse(app.otherElements[@"platform_view[1].overlay[0]"].exists); -} - -// A is the layer above both platform view. -// +------------+ -// | PV +----+ | -// +-----| A |-+ -// +-----| |-+ -// | PV +----+ | -// +------------+ -- (void)testMultiplePlatformViewsWithOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-multiple-background-foreground" ]; - [app launch]; - - XCUIElement* platform_view1 = app.textViews[@"platform_view[8]"]; - XCTAssertTrue(platform_view1.exists); - XCTAssertEqual(platform_view1.frame.origin.x, 25); - XCTAssertEqual(platform_view1.frame.origin.y, 325); - XCTAssertEqual(platform_view1.frame.size.width, 250); - XCTAssertEqual(platform_view1.frame.size.height, 250); - - XCUIElement* platform_view2 = app.textViews[@"platform_view[9]"]; - XCTAssertTrue(platform_view2.exists); - XCTAssertEqual(platform_view2.frame.origin.x, 25); - XCTAssertEqual(platform_view2.frame.origin.y, 25); - XCTAssertEqual(platform_view2.frame.size.width, 250); - XCTAssertEqual(platform_view2.frame.size.height, 250); - - XCUIElement* overlay1 = app.otherElements[@"platform_view[8].overlay[0]"]; - XCTAssertTrue(overlay1.exists); - XCTAssertEqual(overlay1.frame.origin.x, 25); - XCTAssertEqual(overlay1.frame.origin.y, 325); - XCTAssertEqual(overlay1.frame.size.width, 225); - XCTAssertEqual(overlay1.frame.size.height, 175); - - XCUIElement* overlay2 = app.otherElements[@"platform_view[9].overlay[0]"]; - XCTAssertTrue(overlay2.exists); - XCTAssertEqual(overlay2.frame.origin.x, 25); - XCTAssertEqual(overlay2.frame.origin.y, 25); - XCTAssertEqual(overlay2.frame.size.width, 225); - XCTAssertEqual(overlay2.frame.size.height, 250); -} - -// More then two overlays are merged into a single layer. -// +---------------------+ -// | +---+ +---+ +---+ | -// | | A | | B | | C | | -// | +---+ +---+ +---+ | -// | +-------+ | -// +-| D |-----------+ -// +-------+ -- (void)testPlatformViewsMaxOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-max-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 75); - XCTAssertEqual(overlay.frame.origin.y, 85); - XCTAssertEqual(overlay.frame.size.width, 150); - XCTAssertEqual(overlay.frame.size.height, 190); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png index 9ec19ab474f03b6cc7786cb038ee3b3e6d0c51a4..a193faeb040223e56343903c777f9181edb5bcc0 100644 GIT binary patch literal 30022 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ΠAr29j`n?8;-QZE z4jSFDu$|OBK+1@&kY%gDyh4f+et*UW!xwP*gQNm<3K_Yi(Xv2`G0u{G@CDL_`Za|R z0&ba{A^qz!h73(?gBQ zaOaF%@BB*^QHtUx^a&4#swelbeQ@;feaTS45;*zevVw|@B_*Du_fg$vTTApLhT z;(~N8NauodE=cEg)wx}DZdaZ22H-9H%#@2te_y_G)xmx1@#nP+_vs7wCD1Qx;69A{ z-@X&;4$L>JfxjakBUrV-?pPC7bYAj`F8b4z{e%L^L5)Ap&Ej}pv9OANp0FJk6lCu2 z`qwMCQ-p#C$T2zwwupZl2?j`8#a1jqb& z0=E@=#V?uu=M`PWHYtj}0{Y_#&Wfu+&iGHHKM&yc@?V(~vi|c9FG*CCezoXdPx+n% zIcrwd|B-1q@2k?|%s;R2x|fU6rSJXgF(6k7@Me>%1g;WLALUvCG}*aI;3|QWvbdJO zwFIsuaDJV@wFIsua4msbJ8%mq_&GOM30x(hZ4Is^K;6hy0#^x~-^Fq*;eTWaOX`;t XeZBM9{0{PeD?M`X*ny1wR>A)dG2_6W literal 20295 zcmeHuX*iVa8@HyJnz3Yzr3E$ieMzzvCQH_kA+oDbk8B}3O<5*oDSL~pkUjg(q>u%s=030UI)CT+JD2M|+}G1lqoY1XO+`gTr=hN* zPelcrrJ_PeAz|PX)smMN!DXMTzM2wMUK`sa_>ZTJv4-u13si#O9!Z7RhoRaJJp%qx z?K?(=pxjeYY3^hF_g;S=|DR{Tnjtr+;D4Sm0axfxB=`Z+f3C1(*xx5$v&sAa-Xo-- zN15f^xWEPFtbW;*ii(8?`q`(U&pi)5WV@lFYXGj$#-Yno@DKm)6}p!srTj#Pg6l&K zm2(E3`=@BVmW3Qoo zEAE=V()MiS$ENStBiA{d&TI4$Uu9`u;g8Qjx^L@!$Qxhsl75%>84gx2Zkt9p zRMxIl)orxYi*C=;2XrpKI37oy58lCFSM-=&@xF=oTnrW3#;5$UiQAH?R`gyXGY?cu zrq>A6T!x{2ge|-jD(&p3OzZK`!Cx68%Q=PKgb{}sqw2|M$ zql9LA7VneSbIERwo4HO~Pv=4E+IARkA+J5RTrU5ixIMDd-Gj|3_n70e{kVRPqm%YbIrEf=)%WS0xw@4K>=zfOA>tKgx*wpiLK}#s8`F)Z8tF7a!t7z7<4>CBITKz z!{_m5FWzUiD$~o$dd}A|mHK|YyyH4I(Bw_OI$M<_R7_9vIen|xbfYUnVx`aZZ%(5}4Y;&(4*>&L_jytIj{Ezty%+*$Wvj~__PCB!-R41mQZ@4VB z(*JF1Rc{KP5FV~<_snHf?8gPCb<3b_3%5`07E%0D?E@JS){IU*z3tSj^fc0a9lab$ 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 diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png index a2e9351d385e0b2f7800ec71448a05e2736e196e..793082f8f0f7c4edaa29fd14c5ee5a782d1e69b1 100644 GIT binary patch literal 33727 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%0D$f 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 HeDr?+Nh376 literal 22360 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 mf=d=$vf%&Cf@1cqGyR|38zWqd;cH7-*4ss diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 0ab3f7c5352f1..494d6585aa7fe 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -19,12 +19,6 @@ import 'src/touches_scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1), 'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2), 'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3), diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index fcea41a65f7d5..0dde98fd9eebb 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -48,224 +48,6 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin } } -/// A simple platform view with overlay that doesn't intersect with the platform view. -class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewNoOverlayIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - finishBuilderByAddingPlatformViewAndPicture( - builder, - 0, - overlayOffset: const Offset(150, 350), - ); - } -} - -/// A simple platform view with an overlay that partially intersects with the platform view. -class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewPartialIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - finishBuilderByAddingPlatformViewAndPicture( - builder, - 0, - overlayOffset: const Offset(150, 250), - ); - } -} - -/// A simple platform view with two overlays that intersect with each other and the platform view. -class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// A simple platform view with one overlay and two overlays that intersect with each other and the platform view. -class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewOneOverlayTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// Two platform views without an overlay intersecting either platform view. -class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - MultiPlatformViewWithoutOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - builder.pushOffset(0, 600); - _addPlatformViewtoScene(builder, 0, 500, 500); - builder.pop(); - - _addPlatformViewtoScene(builder, 1, 500, 500); - - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTRB(0, 0, 100, 1000), - Paint()..color = const Color(0xFFFF0000), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(580, 0), picture); - - builder.pop(); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// A simple platform view with too many overlays result in a single native view. -class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewMaxOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, -80), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - /// Builds a scene with 2 platform views. class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. @@ -642,17 +424,12 @@ mixin _BasePlatformViewScenarioMixin on Scenario { } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture( - SceneBuilder sceneBuilder, - int viewId, { - Offset overlayOffset, - }) { - overlayOffset ??= const Offset(50, 50); + void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( - overlayOffset, + const Offset(50, 50), 50, Paint()..color = const Color(0xFFABCDEF), ); From f58967fbd33605071ff07dd3fb06769313ae1447 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 20 Mar 2020 15:56:05 -0700 Subject: [PATCH 066/386] Re-land "Enable unified OpenGL/Metal builds." (#17230) --- tools/gn | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tools/gn b/tools/gn index 0fb187febda35..6d47127ca30fe 100755 --- a/tools/gn +++ b/tools/gn @@ -44,9 +44,6 @@ def get_out_dir(args): if args.enable_vulkan: target_dir.append('vulkan') - if args.enable_metal and args.target_os == 'ios': - target_dir.append('metal') - return os.path.join(args.out_dir, 'out', '_'.join(target_dir)) def to_command_line(gn_args): @@ -219,7 +216,8 @@ def to_gn_args(args): gn_args['use_goma'] = False gn_args['goma_dir'] = None - if args.enable_metal: + # Enable Metal on non-simulator iOS builds. + if args.target_os == 'ios' and not args.simulator: gn_args['skia_use_metal'] = True gn_args['shell_enable_metal'] = True # Bitcode enabled builds using the current version of the toolchain leak @@ -323,7 +321,6 @@ def parse_args(args): parser.add_argument('--operator-new-alignment', dest='operator_new_alignment', type=str, default=None) parser.add_argument('--enable-vulkan', action='store_true', default=False) - parser.add_argument('--enable-metal', action='store_true', default=False) parser.add_argument('--enable-fontconfig', action='store_true', default=False) parser.add_argument('--enable-skshaper', action='store_true', default=False) From e2ec94b84d1a2517bf852be78a95750c769d91a5 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 19:01:05 -0400 Subject: [PATCH 067/386] Roll src/third_party/skia 7b7b78cb9f1b..933926683991 (6 commits) (#17232) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 02e094c7662ba..ee6c5db598713 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '7b7b78cb9f1bd8b57fb849e5cae7f477e84d3cf6', + 'skia_revision': '93392668399146a92385e2a01857de669f5b956f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 534df3431875d..0684d15a39525 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1abbcf1f0b0817a1a80805bd2b8db4d9 +Signature: 412a77245e4aacb2e6da919892f78a6b UNUSED LICENSES: From 47ae84beba0ae3e2113ec7109ddb064d819098b6 Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 20 Mar 2020 16:58:52 -0700 Subject: [PATCH 068/386] Run web framework tests on third-party rolls (#17238) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 98ff5be33f3a9..441e3ceec3937 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,7 +1,7 @@ gcp_credentials: ENCRYPTED[987a78af29b91ce8489594c9ab3fec21845bbe5ba68294b8f6def3cf0d380830f06687a89ea69c87344c5ade369700fe] web_shard_template: &WEB_SHARD_TEMPLATE - only_if: "changesInclude('.cirrus.yml', 'lib/web_ui/**', 'web_sdk/**') || $CIRRUS_PR == ''" + only_if: "changesInclude('.cirrus.yml', 'DEPS', 'lib/web_ui/**', 'web_sdk/**') || $CIRRUS_PR == ''" environment: # As of March 2020, the Web shards needed 16G of RAM and 4 CPUs to run all framework tests with goldens without flaking. CPU: 4 From 1152709f71ffd7ffdeec5de8a12af79326ee69f1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 20:01:03 -0400 Subject: [PATCH 069/386] Roll fuchsia/sdk/core/linux-amd64 from ZmlfS... to pTJOm... (#17235) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ee6c5db598713..0e8e8605a5d72 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'ZmlfSUYgB02RndQVLenzGehstcM7ASCSaD9L8DETVX8C' + 'version': 'pTJOmnMiJ9nHE2-CdHz0kzE6OgLnT5ANiLVR--vNFvYC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 75fcccf9db28a..6cb6d8f82516e 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 8d4a75deacb75655c9faf5686550c648 +Signature: 08e886dba87b26ec51aea03fe463fec9 UNUSED LICENSES: From 060cbc448738a113601fe8f093df3e1f088af00a Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 20 Mar 2020 17:02:59 -0700 Subject: [PATCH 070/386] Revert "Roll src/third_party/dart ba8baa46b452..0296286c03f6 (11 commits) (#17226)" (#17239) This reverts commit 6c757f3bba59fb86b6dc0dcd96266e06eec562f0. It broke web tests with: ``` org-dartlang-app:///test/material/icon_button_test.dart:15:7: Warning: Implementing 'Function' is deprecated. Try removing 'Function' from the 'implements' clause. class MockOnPressedFunction implements Function { ``` --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 0e8e8605a5d72..459afbd1b0101 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '0296286c03f6d7d9db272fc12a1e0c1a4957c69f', + 'dart_revision': 'ba8baa46b4527a631c077f5a59c348931688d91a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -77,7 +77,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '11afdac99fe67373829f37acc75b625011d360ad', + 'dart_pub_rev': '429a06039d185149f387a65e3503b0693ce6d24e', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 332c21e291898..4ccdf267b2ce3 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 751019019a61cc75137e78d2f4da5f02 +Signature: bb7475da375e6d3d09b02525bab95d8b UNUSED LICENSES: From 46cc35facd21ce54b2f56179862ec26cf70390b3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 20:06:03 -0400 Subject: [PATCH 071/386] Roll fuchsia/sdk/core/mac-amd64 from MKYwg... to uyg4y... (#17234) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 459afbd1b0101..bc65383852cd4 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'MKYwg75UljQZXx15HwzYoQj3Vxa0KVJ7q1x0XoALCBQC' + 'version': 'uyg4yfQaJJ5IxN824uYTcsiT7_Rk2UX_EGN1dJ9NJ0oC' } ], 'condition': 'host_os == "mac"', From d79170f08710d2326b1d6a960a04fa465781aaf0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 21:16:02 -0400 Subject: [PATCH 072/386] Roll src/third_party/skia 933926683991..e41fa2d5e44d (2 commits) (#17240) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index bc65383852cd4..3d204b33e9132 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '93392668399146a92385e2a01857de669f5b956f', + 'skia_revision': 'e41fa2d5e44d4615948bd769fef400b5017f5a61', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 0684d15a39525..2badbc58b2ba7 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 412a77245e4aacb2e6da919892f78a6b +Signature: 1d64f22b1df81102dce82042616fc41d UNUSED LICENSES: From f5d25da83ceadffea850cae369f95d3fc1fde85f Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 20 Mar 2020 18:58:55 -0700 Subject: [PATCH 073/386] Reland: Implement unobstructed Platform Views on iOS (#17237) --- flow/embedded_views.cc | 5 +- flow/embedded_views.h | 5 +- flow/layers/picture_layer.cc | 2 +- flow/layers/picture_layer_unittests.cc | 3 - shell/common/rasterizer.cc | 12 +- .../framework/Source/FlutterPlatformViews.mm | 320 ++++++++++++------ .../Source/FlutterPlatformViews_Internal.h | 87 ++++- .../Source/FlutterPlatformViews_Internal.mm | 3 +- shell/platform/darwin/ios/ios_surface.h | 5 +- shell/platform/darwin/ios/ios_surface.mm | 12 +- .../embedder_external_view_embedder.cc | 6 +- .../embedder_external_view_embedder.h | 5 +- .../Scenarios.xcodeproj/project.pbxproj | 4 + .../ios/Scenarios/Scenarios/AppDelegate.m | 7 + .../PlatformViewGestureRecognizerTests.m | 6 +- .../UnobstructedPlatformViewTests.m | 254 ++++++++++++++ ...tform_view_clippath_iPhone 8_simulator.png | Bin 30022 -> 20295 bytes ...form_view_transform_iPhone 8_simulator.png | Bin 33727 -> 22360 bytes testing/scenario_app/lib/main.dart | 6 + .../scenario_app/lib/src/platform_view.dart | 227 ++++++++++++- 20 files changed, 842 insertions(+), 127 deletions(-) create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index c660f4691318b..5234bf1e50c8c 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -6,10 +6,13 @@ namespace flutter { -bool ExternalViewEmbedder::SubmitFrame(GrContext* context) { +bool ExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { return false; }; +void ExternalViewEmbedder::FinishFrame(){}; + void MutatorsStack::PushClipRect(const SkRect& rect) { std::shared_ptr element = std::make_shared(rect); vector_.push_back(element); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 030eb88c8a06d..7a491a8a152ef 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -248,7 +248,10 @@ class ExternalViewEmbedder { // Must be called on the UI thread. virtual SkCanvas* CompositeEmbeddedView(int view_id) = 0; - virtual bool SubmitFrame(GrContext* context); + virtual bool SubmitFrame(GrContext* context, SkCanvas* background_canvas); + + // This is called after submitting the embedder frame and the surface frame. + virtual void FinishFrame(); FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 3bc7e394c1033..08c09cc9e833b 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -59,7 +59,7 @@ void PictureLayer::Paint(PaintContext& context) const { return; } } - context.leaf_nodes_canvas->drawPicture(picture()); + picture()->playback(context.leaf_nodes_canvas); } } // namespace flutter diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 687c870eeac66..4f565cf500ecc 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -94,9 +94,6 @@ TEST_F(PictureLayerTest, SimplePicture) { 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( layer_offset_matrix)}}, #endif - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), - SkMatrix()}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 811898b5a42d2..f7e4350fe9c97 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -342,9 +342,17 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { if (raster_status == RasterStatus::kFailed) { return raster_status; } - frame->Submit(); if (external_view_embedder != nullptr) { - external_view_embedder->SubmitFrame(surface_->GetContext()); + external_view_embedder->SubmitFrame(surface_->GetContext(), + root_surface_canvas); + // The external view embedder may mutate the root surface canvas while + // submitting the frame. + // Therefore, submit the final frame after asking the external view + // embedder to submit the frame. + frame->Submit(); + external_view_embedder->FinishFrame(); + } else { + frame->Submit(); } FireNextFrameCallbackIfPresent(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b86c4623fa7a7..b96fcbbbaa76a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -8,16 +8,77 @@ #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include #include #include #include #include "FlutterPlatformViews_Internal.h" +#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" namespace flutter { +std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( + GrContext* gr_context, + std::shared_ptr ios_context) { + if (available_layer_index_ >= layers_.size()) { + std::shared_ptr layer; + + if (!gr_context) { + fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + + layer = std::make_shared( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + } else { + CGFloat screenScale = [UIScreen mainScreen].scale; + fml::scoped_nsobject overlay_view( + [[FlutterOverlayView alloc] initWithContentsScale:screenScale]); + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + + layer = std::make_shared( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + layer->gr_context = gr_context; + } + layers_.push_back(layer); + } + auto layer = layers_[available_layer_index_]; + if (gr_context != layer->gr_context) { + layer->gr_context = gr_context; + // The overlay already exists, but the GrContext was changed so we need to recreate + // the rendering surface with the new GrContext. + IOSSurface* ios_surface = layer->ios_surface.get(); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + layer->surface = std::move(surface); + } + available_layer_index_++; + return layer; +} + +void FlutterPlatformViewLayerPool::RecycleLayers() { + available_layer_index_ = 0; +} + +std::vector> +FlutterPlatformViewLayerPool::GetUnusedLayers() { + std::vector> results; + for (size_t i = available_layer_index_; i < layers_.size(); i++) { + results.push_back(layers_[i]); + } + return results; +} + void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { flutter_view_.reset([flutter_view retain]); } @@ -83,6 +144,9 @@ NSObject* embedded_view = [factory createWithFrame:CGRectZero viewIdentifier:viewId arguments:params]; + // Set a unique view identifier, so the platform view can be identified in unit tests. + [embedded_view view].accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%ld]", viewId]; views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] @@ -196,8 +260,11 @@ int view_id, std::unique_ptr params) { picture_recorders_[view_id] = std::make_unique(); - picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_)); - picture_recorders_[view_id]->getRecordingCanvas()->clear(SK_ColorTRANSPARENT); + + auto rtree_factory = RTreeFactory(); + platform_view_rtrees_[view_id] = rtree_factory.getInstance(); + picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_), &rtree_factory); + composition_order_.push_back(view_id); if (current_composition_params_.count(view_id) == 1 && @@ -361,77 +428,182 @@ composition_order_.clear(); active_composition_order_.clear(); picture_recorders_.clear(); + platform_view_rtrees_.clear(); current_composition_params_.clear(); clip_count_.clear(); views_to_recomposite_.clear(); + layer_pool_->RecycleLayers(); +} + +SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) { + UIView* platform_view = [views_[view_id].get() view]; + UIScreen* screen = [UIScreen mainScreen]; + CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds + toView:flutter_view_]; + return SkRect::MakeXYWH(platform_view_cgrect.origin.x * screen.scale, // + platform_view_cgrect.origin.y * screen.scale, // + platform_view_cgrect.size.width * screen.scale, // + platform_view_cgrect.size.height * screen.scale // + ); } bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context) { + std::shared_ptr ios_context, + SkCanvas* background_canvas) { DisposeViews(); - - bool did_submit = true; - for (int64_t view_id : composition_order_) { - EnsureOverlayInitialized(view_id, ios_context, gr_context); - auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); - // If frame is null, AcquireFrame already printed out an error message. - if (frame) { - SkCanvas* canvas = frame->SkiaCanvas(); - canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); - canvas->flush(); - did_submit &= frame->Submit(); + // Clipping the background canvas before drawing the picture recorders requires to + // save and restore the clip context. + SkAutoCanvasRestore save(background_canvas, /*doSave=*/true); + // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. + LayersMap platform_view_layers; + + auto did_submit = true; + auto num_platform_views = composition_order_.size(); + + for (size_t i = 0; i < num_platform_views; i++) { + int64_t platform_view_id = composition_order_[i]; + sk_sp rtree = platform_view_rtrees_[platform_view_id]; + sk_sp picture = picture_recorders_[platform_view_id]->finishRecordingAsPicture(); + + // Check if the current picture contains overlays that intersect with the + // current platform view or any of the previous platform views. + for (size_t j = i + 1; j > 0; j--) { + int64_t current_platform_view_id = composition_order_[j - 1]; + SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); + std::list intersection_rects = + rtree->searchNonOverlappingDrawnRects(platform_view_rect); + auto allocation_size = intersection_rects.size(); + + // For testing purposes, the overlay id is used to find the overlay view. + // This is the index of the layer for the current platform view. + auto overlay_id = platform_view_layers[current_platform_view_id].size(); + + // If the max number of allocations per platform view is exceeded, + // then join all the rects into a single one. + // + // TODO(egarciad): Consider making this configurable. + // https://github.com/flutter/flutter/issues/52510 + if (allocation_size > kMaxLayerAllocations) { + SkRect joined_rect; + for (const SkRect& rect : intersection_rects) { + joined_rect.join(rect); + } + // Replace the rects in the intersection rects list for a single rect that is + // the union of all the rects in the list. + intersection_rects.clear(); + intersection_rects.push_back(joined_rect); + } + for (SkRect& joined_rect : intersection_rects) { + // Get the intersection rect between the current rect + // and the platform view rect. + joined_rect.intersect(platform_view_rect); + // Clip the background canvas, so it doesn't contain any of the pixels drawn + // on the overlay layer. + background_canvas->clipRect(joined_rect, SkClipOp::kDifference); + // Get a new host layer. + auto layer = GetLayer(gr_context, // + ios_context, // + picture, // + joined_rect, // + current_platform_view_id, // + overlay_id // + ); + did_submit &= layer->did_submit_last_frame; + platform_view_layers[current_platform_view_id].push_back(layer); + overlay_id++; + } } - } - picture_recorders_.clear(); - if (composition_order_ == active_composition_order_) { - composition_order_.clear(); - return did_submit; - } - DetachUnusedLayers(); - active_composition_order_.clear(); - UIView* flutter_view = flutter_view_.get(); + background_canvas->drawPicture(picture); + } + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(); + // Organize the layers by their z indexes. + BringLayersIntoView(platform_view_layers); + // Mark all layers as available, so they can be used in the next frame. + layer_pool_->RecycleLayers(); + // Reset the composition order, so next frame starts empty. + composition_order_.clear(); + + return did_submit; +} +void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { + UIView* flutter_view = flutter_view_.get(); + auto zIndex = 0; for (size_t i = 0; i < composition_order_.size(); i++) { - int view_id = composition_order_[i]; - // We added a chain of super views to the platform view to handle clipping. - // The `platform_view_root` is the view at the top of the chain which is a direct subview of the - // `FlutterView`. - UIView* platform_view_root = root_views_[view_id].get(); - UIView* overlay = overlays_[view_id]->overlay_view; - FML_CHECK(platform_view_root.superview == overlay.superview); - if (platform_view_root.superview == flutter_view) { - [flutter_view bringSubviewToFront:platform_view_root]; - [flutter_view bringSubviewToFront:overlay]; - } else { + int64_t platform_view_id = composition_order_[i]; + std::vector> layers = layer_map[platform_view_id]; + UIView* platform_view_root = root_views_[platform_view_id].get(); + + if (platform_view_root.superview != flutter_view) { [flutter_view addSubview:platform_view_root]; - [flutter_view addSubview:overlay]; - overlay.frame = flutter_view.bounds; + } else { + platform_view_root.layer.zPosition = zIndex++; } - - active_composition_order_.push_back(view_id); + for (const std::shared_ptr& layer : layers) { + if ([layer->overlay_view superview] != flutter_view) { + [flutter_view addSubview:layer->overlay_view]; + } else { + layer->overlay_view.get().layer.zPosition = zIndex++; + } + } + active_composition_order_.push_back(platform_view_id); } - composition_order_.clear(); - return did_submit; } -void FlutterPlatformViewsController::DetachUnusedLayers() { +std::shared_ptr FlutterPlatformViewsController::GetLayer( + GrContext* gr_context, + std::shared_ptr ios_context, + sk_sp picture, + SkRect rect, + int64_t view_id, + int64_t overlay_id) { + auto layer = layer_pool_->GetLayer(gr_context, ios_context); + auto screenScale = [UIScreen mainScreen].scale; + // Set the size of the overlay UIView. + layer->overlay_view.get().frame = CGRectMake(rect.x() / screenScale, // + rect.y() / screenScale, // + rect.width() / screenScale, // + rect.height() / screenScale // + ); + // Set a unique view identifier, so the overlay can be identified in unit tests. + layer->overlay_view.get().accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; + + std::unique_ptr frame = + layer->surface->AcquireFrame(SkISize::Make(rect.width(), rect.height())); + // If frame is null, AcquireFrame already printed out an error message. + if (!frame) { + return layer; + } + auto overlay_canvas = frame->SkiaCanvas(); + overlay_canvas->clear(SK_ColorTRANSPARENT); + // Offset the picture since its absolute position on the scene is determined + // by the position of the overlay view. + overlay_canvas->translate(-rect.x(), -rect.y()); + overlay_canvas->drawPicture(picture); + + layer->did_submit_last_frame = frame->Submit(); + return layer; +} + +void FlutterPlatformViewsController::RemoveUnusedLayers() { + auto layers = layer_pool_->GetUnusedLayers(); + for (const std::shared_ptr& layer : layers) { + [layer->overlay_view removeFromSuperview]; + } + std::unordered_set composition_order_set; for (int64_t view_id : composition_order_) { composition_order_set.insert(view_id); } - + // Remove unused platform views. for (int64_t view_id : active_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { - if (root_views_.find(view_id) == root_views_.end()) { - continue; - } - // We added a chain of super views to the platform view to handle clipping. - // The `platform_view_root` is the view at the top of the chain which is a direct subview of - // the `FlutterView`. UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; - [overlays_[view_id]->overlay_view.get() removeFromSuperview]; } } } @@ -458,56 +630,6 @@ views_to_dispose_.clear(); } -void FlutterPlatformViewsController::EnsureOverlayInitialized( - int64_t overlay_id, - std::shared_ptr ios_context, - GrContext* gr_context) { - FML_DCHECK(flutter_view_); - - auto overlay_it = overlays_.find(overlay_id); - - if (!gr_context) { - if (overlays_.count(overlay_id) != 0) { - return; - } - fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); - overlay_view.get().frame = flutter_view_.get().bounds; - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - overlays_[overlay_id] = std::make_unique( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - return; - } - - if (overlay_it != overlays_.end()) { - FlutterPlatformViewLayer* overlay = overlay_it->second.get(); - if (gr_context != overlay->gr_context) { - overlay->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = overlay_it->second->ios_surface.get(); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - overlay_it->second->surface = std::move(surface); - } - return; - } - auto contentsScale = flutter_view_.get().layer.contentsScale; - fml::scoped_nsobject overlay_view( - [[FlutterOverlayView alloc] initWithContentsScale:contentsScale]); - overlay_view.get().frame = flutter_view_.get().bounds; - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - overlays_[overlay_id] = std::make_unique( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - overlays_[overlay_id]->gr_context = gr_context; -} - } // namespace flutter // This recognizers delays touch events from being dispatched to the responder chain until it failed diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index d135d7d2ac290..59dc940c4d126 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #include "flutter/flow/embedded_views.h" +#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" @@ -68,12 +69,52 @@ struct FlutterPlatformViewLayer { std::unique_ptr ios_surface; std::unique_ptr surface; + // Whether a frame for this layer was submitted. + bool did_submit_last_frame; + // The GrContext that is currently used by the overlay surfaces. // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrContext* gr_context; }; +// This class isn't thread safe. +class FlutterPlatformViewLayerPool { + public: + FlutterPlatformViewLayerPool() = default; + ~FlutterPlatformViewLayerPool() = default; + + // Gets a layer from the pool if available, or allocates a new one. + // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. + std::shared_ptr GetLayer(GrContext* gr_context, + std::shared_ptr ios_context); + + // Gets the layers in the pool that aren't currently used. + // This method doesn't mark the layers as unused. + std::vector> GetUnusedLayers(); + + // Marks the layers in the pool as available for reuse. + void RecycleLayers(); + + private: + // The index of the entry in the layers_ vector that determines the beginning of the unused + // layers. For example, consider the following vector: + // _____ + // | 0 | + /// |---| + /// | 1 | <-- available_layer_index_ + /// |---| + /// | 2 | + /// |---| + /// + /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 + /// cannot be reused. + size_t available_layer_index_ = 0; + std::vector> layers_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); +}; + class FlutterPlatformViewsController { public: FlutterPlatformViewsController(); @@ -109,14 +150,37 @@ class FlutterPlatformViewsController { SkCanvas* CompositeEmbeddedView(int view_id); + // The rect of the platform view at index view_id. This rect has been translated into the + // host view coordinate system. Units are device screen pixels. + SkRect GetPlatformViewRect(int view_id); + // Discards all platform views instances and auxiliary resources. void Reset(); - bool SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context); + bool SubmitFrame(GrContext* gr_context, + std::shared_ptr ios_context, + SkCanvas* background_canvas); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: + static const size_t kMaxLayerAllocations = 2; + + using LayersMap = std::map>>; + + // The pool of reusable view layers. The pool allows to recycle layer in each frame. + std::unique_ptr layer_pool_; + + // The platform view's R-tree keyed off the view id, which contains any subsequent + // draw operation until the next platform view or the last leaf node in the layer tree. + // + // The R-trees are deleted by the FlutterPlatformViewsController.reset(). + std::map> platform_view_rtrees_; + + // The platform view's picture recorder keyed off the view id, which contains any subsequent + // operation until the next platform view or the end of the last leaf node in the layer tree. + std::map> picture_recorders_; + fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; fml::scoped_nsobject flutter_view_controller_; @@ -163,19 +227,12 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies; - std::map> picture_recorders_; - void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); - - void DetachUnusedLayers(); // Dispose the views in `views_to_dispose_`. void DisposeViews(); - void EnsureOverlayInitialized(int64_t overlay_id, - std::shared_ptr ios_context, - GrContext* gr_context); // This will return true after pre-roll if any of the embedded views // have mutated for last layer tree. @@ -215,6 +272,20 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from + // the picture on the layer's canvas. + std::shared_ptr GetLayer(GrContext* gr_context, + std::shared_ptr ios_context, + sk_sp picture, + SkRect rect, + int64_t view_id, + int64_t overlay_id); + // Removes overlay views and platform views that aren't needed in the current frame. + void RemoveUnusedLayers(); + // Appends the overlay views and platform view and sets their z index based on the composition + // order. + void BringLayersIntoView(LayersMap layer_map); + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index 9310fa1803f11..21db9530fe0bb 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -20,7 +20,8 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() = default; +FlutterPlatformViewsController::FlutterPlatformViewsController() + : layer_pool_(std::make_unique()){}; FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 58233d8a2f656..8cba9285cfb02 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -77,7 +77,10 @@ class IOSSurface : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context) override; + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index fe85c77f12c2d..f51160e94e471 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -132,12 +132,18 @@ bool IsIosEmbeddedViewsPreviewEnabled() { } // |ExternalViewEmbedder| -bool IOSSurface::SubmitFrame(GrContext* context) { +bool IOSSurface::SubmitFrame(GrContext* context, SkCanvas* background_canvas) { TRACE_EVENT0("flutter", "IOSSurface::SubmitFrame"); FML_CHECK(platform_views_controller_ != nullptr); - bool submitted = platform_views_controller_->SubmitFrame(std::move(context), ios_context_); - [CATransaction commit]; + bool submitted = + platform_views_controller_->SubmitFrame(std::move(context), ios_context_, background_canvas); return submitted; } +// |ExternalViewEmbedder| +void IOSSurface::FinishFrame() { + TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + [CATransaction commit]; +} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 5e77073e7ff47..23658888f7caa 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -129,7 +129,8 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( } // |ExternalViewEmbedder| -bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { +bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -265,4 +266,7 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { return true; } +// |ExternalViewEmbedder| +void EmbedderExternalViewEmbedder::FinishFrame() {} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 7000d2cde04cd..63c944a88d7ef 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -89,7 +89,10 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context) override; + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index c24333a3a8a7f..818d902b3e2e9 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 3DEF491A23C3BE6500184216 /* golden_platform_view_transform_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */; }; 59A97FD8236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */; }; 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */; }; + 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */; }; 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */; }; @@ -149,6 +150,7 @@ 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone 8_simulator.png"; sourceTree = ""; }; 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_iPhone SE_simulator.png"; sourceTree = ""; }; 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png"; sourceTree = ""; }; + 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnobstructedPlatformViewTests.m; sourceTree = ""; }; 6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenPlatformViewTests.h; sourceTree = ""; }; 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenPlatformViewTests.m; sourceTree = ""; }; 6816DB9F2317573300A51400 /* GoldenImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenImage.h; sourceTree = ""; }; @@ -245,6 +247,7 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( + 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */, 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 3DE09E8B23C010BC006C9851 /* golden_platform_view_clippath_iPhone 8_simulator.png */, 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */, @@ -488,6 +491,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 348889b19b856..9bd732f647039 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -29,6 +29,13 @@ - (BOOL)application:(UIApplication*)application // the launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. NSDictionary* launchArgsMap = @{ @"--platform-view" : @"platform_view", + @"--platform-view-no-overlay-intersection" : @"platform_view_no_overlay_intersection", + @"--platform-view-two-intersecting-overlays" : @"platform_view_two_intersecting_overlays", + @"--platform-view-partial-intersection" : @"platform_view_partial_intersection", + @"--platform-view-one-overlay-two-intersecting-overlays" : + @"platform_view_one_overlay_two_intersecting_overlays", + @"--platform-view-multiple-without-overlays" : @"platform_view_multiple_without_overlays", + @"--platform-view-max-overlays" : @"platform_view_max_overlays", @"--platform-view-multiple" : @"platform_view_multiple", @"--platform-view-multiple-background-foreground" : @"platform_view_multiple_background_foreground", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m index d791210f22707..3d583e1d5e824 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -25,7 +25,7 @@ - (void)testRejectPolicyUtilTouchesEnded { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"platform_view"]; + return [element.identifier hasPrefix:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { @@ -56,7 +56,7 @@ - (void)testRejectPolicyEager { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"platform_view"]; + return [element.identifier hasPrefix:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { @@ -91,7 +91,7 @@ - (void)testAccept { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"platform_view"]; + return [element.identifier hasPrefix:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m new file mode 100644 index 0000000000000..02e7eee35f098 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m @@ -0,0 +1,254 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface UnobstructedPlatformViewTests : XCTestCase + +@end + +@implementation UnobstructedPlatformViewTests + +- (void)setUp { + self.continueAfterFailure = NO; +} + +// A is the layer, which z index is higher than the platform view. +// +--------+ +// | PV | +---+ +// +--------+ | A | +// +---+ +- (void)testNoOverlay { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-no-overlay-intersection" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertFalse(overlay.exists); +} + +// A is the layer above the platform view. +// +-----------------+ +// | PV +---+ | +// | | A | | +// | +---+ | +// +-----------------+ +- (void)testOneOverlay { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 150); + XCTAssertEqual(overlay.frame.origin.y, 150); + XCTAssertEqual(overlay.frame.size.width, 50); + XCTAssertEqual(overlay.frame.size.height, 50); +} + +// A is the layer above the platform view. +// +-----------------+ +// | PV +---+ | +// +-----------| A |-+ +// +---+ +- (void)testOneOverlayPartialIntersection { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-partial-intersection" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 200); + XCTAssertEqual(overlay.frame.origin.y, 250); + XCTAssertEqual(overlay.frame.size.width, 50); + // Half the height of the overlay. + XCTAssertEqual(overlay.frame.size.height, 25); +} + +// A and B are the layers above the platform view. +// +--------------------+ +// | PV +------------+ | +// | | B +-----+ | | +// | +---| A |-+ | +// +----------| |---+ +// +-----+ +- (void)testTwoIntersectingOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-two-intersecting-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 150); + XCTAssertEqual(overlay.frame.origin.y, 150); + XCTAssertEqual(overlay.frame.size.width, 75); + XCTAssertEqual(overlay.frame.size.height, 75); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); +} + +// A, B, and C are the layers above the platform view. +// +-------------------------+ +// | PV +-----------+ | +// | +---+ | B +-----+ | | +// | | C | +---| A |-+ | +// | +---+ +-----+ | +// +-------------------------+ +- (void)testOneOverlayAndTwoIntersectingOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-one-overlay-two-intersecting-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay1.exists); + XCTAssertEqual(overlay1.frame.origin.x, 150); + XCTAssertEqual(overlay1.frame.origin.y, 150); + XCTAssertEqual(overlay1.frame.size.width, 75); + XCTAssertEqual(overlay1.frame.size.height, 75); + + XCUIElement* overlay2 = app.otherElements[@"platform_view[0].overlay[1]"]; + XCTAssertTrue(overlay2.exists); + XCTAssertEqual(overlay2.frame.origin.x, 75); + XCTAssertEqual(overlay2.frame.origin.y, 225); + XCTAssertEqual(overlay2.frame.size.width, 50); + XCTAssertEqual(overlay2.frame.size.height, 50); +} + +// A is the layer, which z index is higher than the platform view. +// +--------+ +// | PV | +---+ +// +--------+ | A | +// +--------+ +---+ +// | PV | +// +--------+ +- (void)testMultiplePlatformViewsWithoutOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-multiple-without-overlays" ]; + [app launch]; + + XCUIElement* platform_view1 = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view1.exists); + XCTAssertEqual(platform_view1.frame.origin.x, 25); + XCTAssertEqual(platform_view1.frame.origin.y, 325); + XCTAssertEqual(platform_view1.frame.size.width, 250); + XCTAssertEqual(platform_view1.frame.size.height, 250); + + XCUIElement* platform_view2 = app.textViews[@"platform_view[1]"]; + XCTAssertTrue(platform_view2.exists); + XCTAssertEqual(platform_view2.frame.origin.x, 25); + XCTAssertEqual(platform_view2.frame.origin.y, 25); + XCTAssertEqual(platform_view2.frame.size.width, 250); + XCTAssertEqual(platform_view2.frame.size.height, 250); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[0]"].exists); + XCTAssertFalse(app.otherElements[@"platform_view[1].overlay[0]"].exists); +} + +// A is the layer above both platform view. +// +------------+ +// | PV +----+ | +// +-----| A |-+ +// +-----| |-+ +// | PV +----+ | +// +------------+ +- (void)testMultiplePlatformViewsWithOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-multiple-background-foreground" ]; + [app launch]; + + XCUIElement* platform_view1 = app.textViews[@"platform_view[8]"]; + XCTAssertTrue(platform_view1.exists); + XCTAssertEqual(platform_view1.frame.origin.x, 25); + XCTAssertEqual(platform_view1.frame.origin.y, 325); + XCTAssertEqual(platform_view1.frame.size.width, 250); + XCTAssertEqual(platform_view1.frame.size.height, 250); + + XCUIElement* platform_view2 = app.textViews[@"platform_view[9]"]; + XCTAssertTrue(platform_view2.exists); + XCTAssertEqual(platform_view2.frame.origin.x, 25); + XCTAssertEqual(platform_view2.frame.origin.y, 25); + XCTAssertEqual(platform_view2.frame.size.width, 250); + XCTAssertEqual(platform_view2.frame.size.height, 250); + + XCUIElement* overlay1 = app.otherElements[@"platform_view[8].overlay[0]"]; + XCTAssertTrue(overlay1.exists); + XCTAssertEqual(overlay1.frame.origin.x, 25); + XCTAssertEqual(overlay1.frame.origin.y, 325); + XCTAssertEqual(overlay1.frame.size.width, 225); + XCTAssertEqual(overlay1.frame.size.height, 175); + + XCUIElement* overlay2 = app.otherElements[@"platform_view[9].overlay[0]"]; + XCTAssertTrue(overlay2.exists); + XCTAssertEqual(overlay2.frame.origin.x, 25); + XCTAssertEqual(overlay2.frame.origin.y, 25); + XCTAssertEqual(overlay2.frame.size.width, 225); + XCTAssertEqual(overlay2.frame.size.height, 250); +} + +// More then two overlays are merged into a single layer. +// +---------------------+ +// | +---+ +---+ +---+ | +// | | A | | B | | C | | +// | +---+ +---+ +---+ | +// | +-------+ | +// +-| D |-----------+ +// +-------+ +- (void)testPlatformViewsMaxOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-max-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 75); + XCTAssertEqual(overlay.frame.origin.y, 85); + XCTAssertEqual(overlay.frame.size.width, 150); + XCTAssertEqual(overlay.frame.size.height, 190); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png index a193faeb040223e56343903c777f9181edb5bcc0..9ec19ab474f03b6cc7786cb038ee3b3e6d0c51a4 100644 GIT binary patch literal 20295 zcmeHuX*iVa8@HyJnz3Yzr3E$ieMzzvCQH_kA+oDbk8B}3O<5*oDSL~pkUjg(q>u%s=030UI)CT+JD2M|+}G1lqoY1XO+`gTr=hN* zPelcrrJ_PeAz|PX)smMN!DXMTzM2wMUK`sa_>ZTJv4-u13si#O9!Z7RhoRaJJp%qx z?K?(=pxjeYY3^hF_g;S=|DR{Tnjtr+;D4Sm0axfxB=`Z+f3C1(*xx5$v&sAa-Xo-- zN15f^xWEPFtbW;*ii(8?`q`(U&pi)5WV@lFYXGj$#-Yno@DKm)6}p!srTj#Pg6l&K zm2(E3`=@BVmW3Qoo zEAE=V()MiS$ENStBiA{d&TI4$Uu9`u;g8Qjx^L@!$Qxhsl75%>84gx2Zkt9p zRMxIl)orxYi*C=;2XrpKI37oy58lCFSM-=&@xF=oTnrW3#;5$UiQAH?R`gyXGY?cu zrq>A6T!x{2ge|-jD(&p3OzZK`!Cx68%Q=PKgb{}sqw2|M$ zql9LA7VneSbIERwo4HO~Pv=4E+IARkA+J5RTrU5ixIMDd-Gj|3_n70e{kVRPqm%YbIrEf=)%WS0xw@4K>=zfOA>tKgx*wpiLK}#s8`F)Z8tF7a!t7z7<4>CBITKz z!{_m5FWzUiD$~o$dd}A|mHK|YyyH4I(Bw_OI$M<_R7_9vIen|xbfYUnVx`aZZ%(5}4Y;&(4*>&L_jytIj{Ezty%+*$Wvj~__PCB!-R41mQZ@4VB z(*JF1Rc{KP5FV~<_snHf?8gPCb<3b_3%5`07E%0D?E@JS){IU*z3tSj^fc0a9lab$ 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 literal 30022 zcmeHwcT|&E^M8OKAo_x!U=M3Ur36J$dK48E5F5P&8=&-F0|XTdD7#7#=_)EHMd=+x zLFpDiAS56HN=c9wTF7snCzgHB`TqWW|H_`TXHD+Cb7$sG`OMrK_r~#K+DjL&UW~zD zmL55L;3NjaOUGdNl>`^TH??hw$bYal`}Fo6 zEPNI84TJGez+eWgFc_747>uNIT+siF3^$q9@75FpqHGYE>I>p^i zeS@*yaVae)*9%g+<#)*|Y|vONB_*Zidfr;)VJCj)PC1w%=jP++e#TqQ(QPA#$S*nvF1Vd@z3A+I(aBK?p?l`6lZU(d zh7HI@zyIOz>3-4r&rXhRKg9w8@yG~%hr9y*H*Hv|hJ34{?|Sh9?2ORY*rA5b{IBnR z?xTiBHvbnh97)k{K~#;!YWUx3(^#zgO4xKgc26y8F)dWSQ2tgPgUaX+#$}?oUL*9;$5B>ZM0~LO} z!Npo+&NzG~@L{pcMY4CciTz$BN?FGTi+_GzEhSa3$Lb&5e||3s>ZY&!Jz1(5!&6zx z6MFvUpCwqYn%|Q#XsbWLS29#x#aAnty-;)VR zJiNhvd=l@L3;kjV2;TlPnRltEC}mC7;m3!6OG$9{TJn3csBKVCH#O+s&Vz_bIE)7u zpZh(Ts{*bSfK_lEfvWs%@fu+-R`(sjtNDJe%)PVUKriXeVp7dS3g*{8m zuTN#aA^Ja`{q)2#W}G?^+EtvHSvGn#@qU2vDU$dtvLe9kX#cTs@9@lbn#efY}MxW|^?!shf z`E+2t4+F<~sGTOT-ka3uXXN{1U)z^caYdy0yMbeOTgIC*6SuL&1HZHkG8Vct6MRUy z(t&eMY;%vVZ*RuDEFYCj$Xxh9ob9O}l2M=e5UoK~-jdf45z(8v)+ms{VAw)&yBcLi`1OIEd$zkA~rc3=5?{-yU1-YGLkeKKA~<>RW%`jUM6_BVsH zh0aU^8KZ<)pUhZk+I00uOXX!5o1^(onx>?F#c^D3$~Q{k!4}%|n#9=I?2@uMdR2>L z`Si#jZlNkMV~TEJL`|a?^rjCcvd5i{sV6ct1IG6RKAL6G${hwDrs&{3Z}(&xvS&1D zW~?T*PmS(O=6vntmh}8G^>N>1=?fZx1B~Zsx7W=L@83+7M$@P8;=R3PQhL<_4wG*;nn_H%FXT(g6}h(yNFS3e9WGU&O@EapKFcVyc1)7lw=h3L z7pZ!ZNZl))6Fs9oT2WcI-!FdLanM-vhqxQxNN>1Pnx*#{R$Q~??aP`o(#34yvzZc_ zQ|B@p+9!tF54J>?`?KSi8BrCq5J>m-;*BmcMOSD8VuY63mF|bHgDqJK8eTPdRjYPBvRAZ5=l$p29WR4+){! z|GJ=i;=K(I(?(8o57^|*J=E+t8LvU=JAU$JUHfO^Oe8K--ZD(CFSpI|-uK zu>mT7Yi1VHbtg}0=6W@L&MYrWn{8vy)~NK5-p4Tq{Ck|xlTmstZOqBzF)m!ADCItj7P)ib_1wq%^0CY&b-$_8b5rU{3>zv3OBsn@ zPbqzNtA9L!Kcm#s&!*GQCJ%2zVYigep7)seHWtIQ*4)O^|6bcEW1&)WI%aUR$4)hR zZ5$)+-H=8Q>6_M*G%pXwhkI~-%Xr9FgfC+@7WkvjuTAq{!*Q4ms6MfZVzq# zn8B;58{&13Gk(amdE+$Z0|e)u1x~(8!!pj*M0xCv6zkCGmWw{7t67^D$Y!v=H+ob! zc{YAZh*J?>Xm6sYI+nB2zuzfio0rY+A6QMRQQgP-=(ZqNDUA1jI-5eAJr%FKZp2*1 zKc^$B=e$9qXmpZ^e7Lfwy`vRgd|TUc_I#6OCo`_vCrFGE$n^4REDn8U7(W!}Kwt1> z`v?e?6gqZ&AIxOs;@pF0DcK8sD)X*Wtc^IIhaE+epFg=+%BO6r@hX(^`e0lR4K_x_fNubh<n~8xTO%`>H zQ^^U^fGRB};JZGGj723xDOKk$w|CnF^e;5VluM_9=mP;h+2*I_8l1`kL!8U#feTHV zMAldRvRx}E19Se#%z)V^%tj%4N=95)z}UMr)=D$)ab23@B^lCv_j`%6DVon~qjke^ ziF1;-)cslw`Vyb1*P1krZEg}-FuVNBp)IdzkAsTFq-3T=u{=9}RbS{!ov3 zZNyD-XP7irad^Xwqn7ezi{Kybd&@qk)e<+Ak6zX8H!pKdnH@7xwp{P~blxnzFuc7j zvx#+{{%lM#4j<@458R`3aK_a>KYBHZHAG~XKTsT69bYI{vH`UdqZQ7n^#(T<5wp%z zg&f0twj@nu7e90DFX>b;DEl_GhuzP7Rz4qHTwM27fcYrymK4;u>;=)HKFOQhI;i*3S#Xx#f zX>*}{zettW!AEpCIKQU}dwv#kPqnD@~t z>Iyc_MZ{(4^377S*NElnM#Vc^G_@v_Wcn< zF7iIv=pU8c%IwNd_Z_Y5OdIQKYHy=^?l?f)yociaa#oW4MI>$PAjMT_qTQygX=bcv zxU$ZHPxHqOmcftqmwjWH-Nmirg}i18U&}}{g@ukx(z(H6 z;Xb9uxRL;pXS8cfU5#d0`D}Y)mqnNR#~bZ957vAYPGgrxXl;A+gAvM%^*6@@eb46> zSa$sr!%78#CJXapzOhhDqZ57fIE&NE)gAX`c#B-7kaT zzt7{%r`(}FjlRvpQO%0ibq>_-?(3_|dZLeyQCVY>L6#>{Wo!zs{n+c{dT{_RV)snz znxWy7V{+ezd(Rv6JC8TisIb>2-v6l3pIkG_UmJb9F5Z;tYxm~U52M?YuEUWG(+GxF zMEQB8$rJM7GxGzQw3)I)UTv@TcxX1nA_uVuFI6ByoxZuwFw?SYh9{QxjY6Xs=onoY z`->d1SnbDa@%Xw|0fougdmPZ(do$0)9X`7?X5WV|9(iY%q)y(qSZ%ud^5wPE&qy8G zyn&}u-RFho)Qj-VO$0xnzj<`3)5(a&USJCBE^$Gs-J}ap`hE$+gbd3bjVM|9Kop|$Ry zm?&lF&hVKyS5mP64{z_bEW5Uh!3r{8Z=u`NO+>SmFDJGe50|cfqnR-mxGY# z%AATS5lyvk64eN(AV&!a(vJw2IdtYV;~(GIbd!9m$0T7=eMU_x%W zC`DX?)Np@y%hX7xxQgA8po%2^+6z0w)u%oub*@ZA7&YB0RUj=p%VN?PZ70PkxtkQ0 zkOva{GE@TQrxi@UqdUlnBZTJ30_zWdOWVxDTO~S8_bOKnQ}RW604Rfh;e892J|gO@ z+gap^4cR(c${;yi@5(^}1X&5>)Uq5|r|Nn7nPK{BSECVVr-Tq@DpM{Ci2UbacODU3a-uu+Ny6&0d(x2} zM3Mt<@SLcTe_w5+LE-7@%NX(x@dEE3XD5fVn{Lx?w{Ju(XcbVuuH(WeWfm>{Dq5Pm zK|z;{fB>7uxK-9Vr6?h=kU3Gwd4Q8^P}(aWS12~ADb{=XF8eK}d{MW?0sC~pB_`c7 z1Bp0SZIIPIiM z5wysWB1mZ^`R|yFG_rhWn=Xw2!5m$JGwxNeFdZ==!kvm_t!wk6C-0Qz!4cWU26bz6 zufVtE(@4xNKKfa2UhGS(C26wddFw3F@*>m$13dWY)^xLH2G0za7KHW)iM|!{9?5Nc zX*#5X93)~%0wvvzc_J%6k7DoeTL!Wwzpk>?N5&kMA!8qc=Yq&f^`%U@!JXbIHYXrP z+OYx7-#f*NPEqduPlz-@>Qr}k<>i=>$t{Rf5@26^=EDuKdx`_+Fr=k=QfA%tF^VlO z&egnplB|i!bpYh*-cR#rv*26X)eL$OjxgJYnlFZY23a2=K@qraXUn@$%6WRUXW6~0 z+fds!Lqy8R>DC~aYl*MqA@8C2yJ4-*EJjUM0*s0vQ|j1NG#IkRab@$jkj(_EI79h6wfz2S&rHBA~Zt%2jac{V|%_zOiE!p zulDV=t0GhHL12LcfsP)od4p=NhLmxY*vh|n@`KkVzfoB6Yp7xa648n#Tg?O%Uj79k zW;4)>Fsw2Mi%?hqbHlWHcjh@52r*#0u*{0DU(b7dd41zY*FHbAletkEG;4JPFxV7N z*rmg~*h5FUC%%7dc@rwBeer1je&k@z$s=>f{7jyt)pshc3+?UHI4gWs1u+RSm_*P) zDrg#WX!j-_-iMo9K{)IeVM%%OmH$P!w>HBpKVv)F`=^qoGhe8KgH${POW2)pz3%*_ z*SnUJmr06JwEfAt;fM|(Otuv~&4)C1>*1=K81e~;dvS4V`@YAlLD@|Pc6%Q`~%{!ow<<>#SW+Sp^~GCdIe?i2RnO5Ua5B5PhAe|!X}?trjwXIgLOSeWwo zpCwvB1aYhc-jZ_V;{A;%NB;mvAI~nP@7}jMgrEfn5h5FL5!}=&3^^2uQ_h(}9~7{w zH)Oeg{}59DX%`Y=tju9-wc$(iA>Z+aJ#7ki{(>{U2`%4<$ejqV4GRaNRi;$$-dmAu z@l2i%qS)1&vixVp7B#em*7{yl{v<_hM5FBP{^s35PD8Nr$1#(&h7sU=I=&=E(_ z;zv+Nb4;ufqNiOakl_|3Cn{w?P)VAQl*((@`uyVHCk|3s4d>1*w1q8#l%$)jpg&(3 zYBPwKeiEo<;DJL&PHNLK`(r_bQ~Vjl^rFYdx=K)n$tYX>VgJ8{fb8^0x2@bjMcY9K z9)0x)_Gkc8nKRDMwQDn!G0GS|hm94JYCcc)7;4?^GuwX*@y4YPLg$PXm`)77rpMT7 zLig-YChl@+mM|KUmIH_3k*+biIB%uRh;HUq%093_g7aliy@o{HDyGJ${ZXqW*xHnx z;^N}xvZOuH8;tvKmB4&)G}!Ow!cYmhv^Wpk<*PXeVcY8V_bt^SWFN;HM~8zV;y3rS-@`Zmx7GkpX*#z-)`243TwA) zM~2a8QCefLjPi5Kg8ut<+oS8k+8=P1KSh?mD)4d(cUIb{5W*X)Vued^ zvf(lt3Yda6ifCL2?|IO%8QB#viK++BjbA+2&U6776GQ-iT_=F9S^@_n&?f?&0(JWf z;=I^ugYN!Vr}b?X*+g`FKTs73{i}c(WG&8nPBzPDynhE(wHOW34T{gvj~sfIpnT+ao`x;hYIDdn}1z=dDtPjvO@bFZR;4E@drE@*@)Ul+4yq)<@ zPVcE52Iv6-2kVzhQZ8H73gsN`&g*ov`@e^h%zMkce|(V`d10{`!3t-0EGdm_5!Vc1gi=azttORt7HMk8Bdku7Bl1S%uMZ6WJW zUmeV^L-q@X{RoyX&mYN!fL@t_0mf1{5YJiWi zMzk^*vr>RRR~R^pSZ&+f>`GRJE?pn5Ct6nwLW1Ud5bTH1*+z~Mwx^b~i|ojqUzs(B z94v%nk?ZorGPUFB5l|lpM-NJM?=S-7 z23G*p1e7WdZ{yFq&m==oNkyT3`%6OZNdSxT#{>3xynk@e!6u81==wMu%laVqTL>>a zyaqFM3waKmhmjD1A)7)}#5`0)#TL{Rq5weL1WSMmIp8Ab1S%TA>ZsK~WBk7dT%bxr z*%N)b-7CsvIl9CGmJm+0g`B?jso_zW=f#ip(&j}^5*b+@Eo&n7YedQZ^YL_I`Fa#H zihk1p{f;X4a`sxwW79R@?2T4(>Tuu7v#U}aOaMbx=Jf(3AJ=BdVGx4edQmfQ(Iit) z6P5~LnU+5qhS-iT&*YD zxrXql0Pssgq;|iP5MB74cOr)Tw4%$4wyx`dI&ug(5WYp$VkGgFOAtQ|s(1z2|CN#k zYGgBDeKQvQBqQg<=r^%g!3n8u73TV8djR<0o`Q5TA1nqhCUmDE#}|2+2O(Jx7}MsA z`N;RH!jc(=UjVEKy31v`4K!xht>8Rzs8n}e0wx7PL(tryVioh5*f!L!Oki)>uxIM( zfeZ63NBu#I%G&uZ&aml1Cy0 z53dmzW2WOH)Y_;>4A2hdSR26zbZMk}6Y<%kjbJqY;rnUb8CWHUj??KD#d*(65B_Vr zpH^%kOz|0rw`kjr7RFPM7Rb4pepB|G6Dble7|JLvk~xjGAJUOnR)_z|XD-;@?#TU! z#!W$Kvg{Z|8{QT$V5#Q#iu8P{shnBf^Ww7x=$@vCh!ej=%&mBEFfLtwTQcZ4~oc7@+uUVKY7 za@?kyfte^XjvYwJI?s*dU(0`i8bXdXNcw#8-Cb!uR8mpICH**Y<0_E5dcNO>=$t6< z#tibzmg5;N__m^-!!s~Eb6UciF+VfjQs_1yG5x6%#Q3J4L@}uD+sRthf%Im30qF@C zF<;*huat{`%#!2>DbG(P5)l~s4(rWbD%Axa1B&&-mCLU zNZgkJe!F*;k2k7vIFZ_NTs|7QEr?m-AP#75b&YHbo-+vY*9{`jU?19*MKD@n498V@ zVFjA-MFoZJp%gSN@pkc6U4y8p!W*2da#G!M*w|UP5B9$<6wzD;SnTtmH00Qk4n@Fv5}dT=Ty$}6ftYR0y1_9%R)z?1E2wO z%~RAlULXM7_{&4>@TsgwJ>UpVKmY0kMdY{ zH#FKvyv>rmkRfYfVbC}Jy(UH2z=J+L@$AyrCqG(zOaObLm>5he@o~%bW71<(^wIcH z%A37Fd{ip6%UK#M>ZJ4hPMTgk4TCE|_J zM+pq`#ftSc*BncH?u|^K&5&gfpZv_@#3)XMeNpT4^H%Zj-V)s!{pxwwEPd5vd)pHc zmod9mhqXUDrdAQinHUX>_Ef1mUQ~qMie(aZsE@uF zYI~kGsKdal(0ZtrH$Hn%e2C%C?3i>lslTq~SUy*_Eat{V#Lx+kAv``@fX1OO<%2a8 zMGpvt0i-Wc`UVDtyR2WY&!wD}ESzWDOVGBY9OS?`<6&r3Prb<1gAQ8}VW8&j7A2DXH_Zp17yyvkRnXZ z`8oMiQe`sf{`b6MdVIhjdW&eA3aYGmRlYDgM6!1{#fuHups*;pbZ=MDxq;&ayU@J; z9+Kgl@fsV-Tt)Ua@)^OfBewX{(7;#~au4kQ~eEEkO4OG1nfaXZ@V+*Xg zxwV!35b5Op$faZ59NI8MP^NlP1NAGkaI=ZN&>VSL@w)cbtyz~8 z$HI)xf|Sma9^ffb3Xg%gMByP$ljQgl@HT_o7j71FD>Z6K6m`@myO1eE_yVSotsC#| z#kvvA)C!#OV2IVbmyN`u=nKuH7GzEx(NV~!=hamW?H3RPNy5*mrw!_Am@1=hWNN%< zvzC5*U~);D(H$)Uf+;M@+<_crasYpAI6b~TC`)Hd8U%+Hx@Iw_ZB#XMTetn zKLyze@|Fs^H|FU_;8XIxzJRJOYB;qEjmVsw8qA2qKS=mTk=sGS4fDt$mT!?mRyAq64RjBJ&*phx&7^ChPF!CEYd}pan~^Qi20M zxGr;&Pj_jJ?-`#ELsk&yjxAl=$-`RLSTACF@t#Ei=vll7|?SPuo5w^$k-`w)9K zHYPX|KW~}8p-4ZobB*YA;peta&YEt62Pk!LZvw`t1bVrpQ|QH@uGUtBzU{b>Zq9U= z-^`0WBB=XjA}!wzoVsrgr;#j09=og~S5}5t5nu3bS;RVa?ISp6`b16*x1Son9s-T8 zLsl2(=v^AwjCg72O5bCBKrpqaZ4A&F4=y%;Y<;|H**&@DFK|ZtoD)e3-UxD@LfX=q zg^Kjumsj7ekR{)U5?}%dZ}>2ZU}J0tB5lc+VlPI9&w(n?#m43@_{S1_m8AZ|qex0m zM;g$UQ@u6eMouL^4mCVneH*?tn4EgQX9pUGLqVH7vSrzHJ_6FkZ=u@+5YVomDZnxj zs~=t?9lL{0EQS7b?(;6MNM)B-A__B32r}$ICrong1_;ZxGF_a0^a7lfqxu!2r4!sh z+$Kj80}55362Kf%0`d+k3Y-T=E&(b@FLo+NtsaoONVme)WELFg>Wa4^n+Qqiypm1v z7l$%Boe&Lq>m&Cd$)jIF@tk{-=0iN1WuK}I@|$4v9#Y{=J{@Th#+MxLG+wlU(5+6^ zku=qDnf>~sm0r9K&0W1A{Tnud4gsPMlj!gA=tV5_B8mxi z%d>tli0rbo>lj&4yR(oMUrH?=YX5H@)(df_93{USA7Sx4kW2`85NYT51W`pdiKmi9 z3hhW7p_P~W!nM#PTw4&ytZ^2o61V-Q zqR^9S&`@l z4Ta+zY4H~l0_J-R@rCh!l#CO)d-rZ6nA0I8|6;7Ex6WJX_2QIHkH0(Nid(QAZvUGY zCx?og)W&twdsl@Kp7GblUodr#_bcS@uk-WD_kA~_2D=KDz`2U_92TWq3wnOA7<(IM z+pv(J=9;yg-QUP*YQfF&-bHW+X~1;D1!qf)EW2la!dbJx$;eNlH311s6)WJ>WyU^@ zpHViOf~)g3#d{~91C`d;xACq`r;9!lr!jTSFb)wC`aeKUMKoZ#3iqGXyGmHo4b6A5 z>EY^Q$8{t=Iil2B;E10IwM-OtTZ%tABd7~+0xSdmz8Hp+)W$$o9lHHDYs>Vbb{_*Y32OT)r8|n5i$&y_YaJ7BY z@j(KHEF%~;7k!!-P`TOD$T$FpG!8rfY>P&lJL5Lw8IFixIJ|T zk)a6CO7YpWCr>FR{U1_vCYM+*hgQyty+vNKn`=ez>SN7&s$FTr=rz;?#0_SD>qkRr z{eOJk(EB5oo2dUm}E5a#fJt>0T>3+Wv5-=!=bQ?9k9t<3NCw+}_LY21{ zui$gO>2uea{u|l>f%B4$>(NAT7%D{CWYpYWwsy0zT&-mEpb(-jOKG|myeg$-s@BG%D-B&_)fUc#8w5LvEvUJJKerh;N=jz z9Bu)|I*|_8IXUE&1OpnJr$*q$otbc~`_r9@3kd1EHsQpn0CDQx=`pv3v$QK$cWQ&S zHUV{^Zz+AuayIKxV~3dSjS4bDX=h2`h$f3BXChUMw!lt6I^1q}ip4BrmfkTGp=Tbqw6;H*mcU27hQ$-U*W_{Z5^KxALKL?GJHcV z*wVJ({V3qk_fZ{v0|CpIA`Vx}NGBOCjgeCcHzNs}}& z_=Yd7z zTO0e7fuqO}2UcW&!w~gs1rs$FrhB!%J>ZANl-NC$xrM=p1|IH=$l5{HbGK5K)BGUY zfN>9jG8pm;0O;CQ7e#Oy=3qImvlMve>=$DXolFvcBpp7Sx`nBJQg6Mc!ss^oEM2Q5 z_0*F^RBcX)D+0V7X#Jr|n`yjW|9Te$EtTk|)7KJW7LT5?>8^{n9Sn%b9$}B++SJp2 zAo{WjgHX53#2l7MyxJvLFR`_)JX&!yQ_Wm2N{UG5BYx~{u7g*oBkcwsZ9~xVK}k^7 zvFe!88Jx)4y~p7)J+q_8!%qCM-EC~g=Hrb8(KJ%ZWH40T;@X+I zlU>@l1SfX}5+E^TeqfjXVhFqPVYr4V>lJ?f)##d`!$AZmsMw~*^p(YlhtQ&4$4H-l z_J-JC-T0WLPedCdKAAnpo@1BHC9n}R%)`q=zHuylji5jUG)ΠAr29j`n?8;-QZE z4jSFDu$|OBK+1@&kY%gDyh4f+et*UW!xwP*gQNm<3K_Yi(Xv2`G0u{G@CDL_`Za|R z0&ba{A^qz!h73(?gBQ zaOaF%@BB*^QHtUx^a&4#swelbeQ@;feaTS45;*zevVw|@B_*Du_fg$vTTApLhT z;(~N8NauodE=cEg)wx}DZdaZ22H-9H%#@2te_y_G)xmx1@#nP+_vs7wCD1Qx;69A{ z-@X&;4$L>JfxjakBUrV-?pPC7bYAj`F8b4z{e%L^L5)Ap&Ej}pv9OANp0FJk6lCu2 z`qwMCQ-p#C$T2zwwupZl2?j`8#a1jqb& z0=E@=#V?uu=M`PWHYtj}0{Y_#&Wfu+&iGHHKM&yc@?V(~vi|c9FG*CCezoXdPx+n% zIcrwd|B-1q@2k?|%s;R2x|fU6rSJXgF(6k7@Me>%1g;WLALUvCG}*aI;3|QWvbdJO zwFIsuaDJV@wFIsua4msbJ8%mq_&GOM30x(hZ4Is^K;6hy0#^x~-^Fq*;eTWaOX`;t XeZBM9{0{PeD?M`X*ny1wR>A)dG2_6W diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png index 793082f8f0f7c4edaa29fd14c5ee5a782d1e69b1..a2e9351d385e0b2f7800ec71448a05e2736e196e 100644 GIT binary patch literal 22360 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 mf=d=$vf%&Cf@1cqGyR|38zWqd;cH7-*4ss literal 33727 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%0D$f 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 HeDr?+Nh376 diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 494d6585aa7fe..0ab3f7c5352f1 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -19,6 +19,12 @@ import 'src/touches_scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1), 'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2), 'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3), diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 0dde98fd9eebb..fcea41a65f7d5 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -48,6 +48,224 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin } } +/// A simple platform view with overlay that doesn't intersect with the platform view. +class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewNoOverlayIntersectionScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + finishBuilderByAddingPlatformViewAndPicture( + builder, + 0, + overlayOffset: const Offset(150, 350), + ); + } +} + +/// A simple platform view with an overlay that partially intersects with the platform view. +class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewPartialIntersectionScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + finishBuilderByAddingPlatformViewAndPicture( + builder, + 0, + overlayOffset: const Offset(150, 250), + ); + } +} + +/// A simple platform view with two overlays that intersect with each other and the platform view. +class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// A simple platform view with one overlay and two overlays that intersect with each other and the platform view. +class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewOneOverlayTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, 200), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// Two platform views without an overlay intersecting either platform view. +class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + MultiPlatformViewWithoutOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + builder.pushOffset(0, 600); + _addPlatformViewtoScene(builder, 0, 500, 500); + builder.pop(); + + _addPlatformViewtoScene(builder, 1, 500, 500); + + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawRect( + const Rect.fromLTRB(0, 0, 100, 1000), + Paint()..color = const Color(0xFFFF0000), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(580, 0), picture); + + builder.pop(); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// A simple platform view with too many overlays result in a single native view. +class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewMaxOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, 200), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, -80), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + /// Builds a scene with 2 platform views. class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. @@ -424,12 +642,17 @@ mixin _BasePlatformViewScenarioMixin on Scenario { } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { + void finishBuilderByAddingPlatformViewAndPicture( + SceneBuilder sceneBuilder, + int viewId, { + Offset overlayOffset, + }) { + overlayOffset ??= const Offset(50, 50); _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( - const Offset(50, 50), + overlayOffset, 50, Paint()..color = const Color(0xFFABCDEF), ); From 047c03bf5cc57ab2d4d1025993b1dbc1b83a78a9 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 20 Mar 2020 22:01:02 -0400 Subject: [PATCH 074/386] Roll src/third_party/dart ba8baa46b452..fbd0c8a46813 (35 commits) (#17241) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 3d204b33e9132..254a7b124fc96 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'ba8baa46b4527a631c077f5a59c348931688d91a', + 'dart_revision': 'fbd0c8a468136ae4e88d54f2f7bb0e830393591b', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -77,7 +77,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '429a06039d185149f387a65e3503b0693ce6d24e', + 'dart_pub_rev': '11afdac99fe67373829f37acc75b625011d360ad', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 4ccdf267b2ce3..1e48310747223 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: bb7475da375e6d3d09b02525bab95d8b +Signature: b2aca8df422a04ebeb57d71809616e27 UNUSED LICENSES: From 48902d146a18cbec5a44576ae1825c117feecf03 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Fri, 20 Mar 2020 19:24:13 -0700 Subject: [PATCH 075/386] Felt add integration (#17190) * adding arguments to felt for running different type of tests * adding test type enums to felt * more changes to felt for testing branches * more additions to code * more changes to felt * adding code for cloning web_drivers * validating test directories. running the integration tests. update the readme file partially * Removing todo lists used for development * addressing reviewers comments * remove unused imports * addressing more reviewer comments * addressing more reviewer comments * addressing reviewer comments. * addressing reviewer comments. * fixing typos * using chromedriverinstaller as a library. Fixing the commit number used from web_installers repo * clean drivers directory after tests finish * addressing more reviewer comments * throwing all critical exceptions instead of logging and returning boolean flags * adding todos for items we can do for making felt easier to use for local development * do not run integration tests on CIs. Added an issue to the TODO. * changing todo's with issues. --- lib/web_ui/dev/README.md | 16 +- lib/web_ui/dev/common.dart | 36 +- lib/web_ui/dev/environment.dart | 6 + lib/web_ui/dev/felt | 6 +- lib/web_ui/dev/felt.dart | 18 + lib/web_ui/dev/integration_tests_manager.dart | 350 ++++++++++++++++++ lib/web_ui/dev/test_runner.dart | 101 ++++- lib/web_ui/dev/utils.dart | 20 + lib/web_ui/pubspec.yaml | 5 + 9 files changed, 532 insertions(+), 26 deletions(-) create mode 100644 lib/web_ui/dev/integration_tests_manager.dart diff --git a/lib/web_ui/dev/README.md b/lib/web_ui/dev/README.md index 5e5f5d45c0832..c79dc1f3d6c46 100644 --- a/lib/web_ui/dev/README.md +++ b/lib/web_ui/dev/README.md @@ -31,12 +31,24 @@ felt build [-w] -j 100 If you are a Google employee, you can use an internal instance of Goma to parallelize your builds. Because Goma compiles code on remote servers, this option is effective even on low-powered laptops. ## Running web engine tests -To run all tests on Chrome: +To run all tests on Chrome. This will run both integration tests and the unit tests: ``` felt test ``` +To run unit tests only: + +``` +felt test --unit-tests-only +``` + +To run integration tests only. For now these tests are only available on Chrome Desktop browsers. + +``` +felt test --integration-tests-only +``` + To run tests on Firefox (this will work only on a Linux device): ``` @@ -55,7 +67,7 @@ To run tests on Safari use the following command. It works on MacOS devices and felt test --browser=safari ``` -To run tests on Windows Edge use the following command. It works on Windows devices and it uses the Edge installed on the OS. +To run tests on Windows Edge use the following command. It works on Windows devices and it uses the Edge installed on the OS. ``` felt_windows.bat test --browser=edge diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 9fa8d126c86ae..37b8e2bf93473 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -27,6 +27,15 @@ class BrowserInstallerException implements Exception { String toString() => message; } +class DriverException implements Exception { + DriverException(this.message); + + final String message; + + @override + String toString() => message; +} + abstract class PlatformBinding { static PlatformBinding get instance { if (_instance == null) { @@ -77,11 +86,10 @@ class _WindowsBinding implements PlatformBinding { @override String getFirefoxDownloadUrl(String version) => 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/${version}/win64/en-US/' - '${getFirefoxDownloadFilename(version)}'; + '${getFirefoxDownloadFilename(version)}'; @override - String getFirefoxDownloadFilename(String version) => - 'firefox-${version}.exe'; + String getFirefoxDownloadFilename(String version) => 'firefox-${version}.exe'; @override String getFirefoxExecutablePath(io.Directory versionDir) => @@ -117,7 +125,7 @@ class _LinuxBinding implements PlatformBinding { @override String getFirefoxDownloadUrl(String version) => 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/${version}/linux-x86_64/en-US/' - '${getFirefoxDownloadFilename(version)}'; + '${getFirefoxDownloadFilename(version)}'; @override String getFirefoxDownloadFilename(String version) => @@ -161,16 +169,15 @@ class _MacBinding implements PlatformBinding { @override String getFirefoxDownloadUrl(String version) => - 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/${version}/mac/en-US/' - '${getFirefoxDownloadFilename(version)}'; + 'https://download-installer.cdn.mozilla.net/pub/firefox/releases/${version}/mac/en-US/' + '${getFirefoxDownloadFilename(version)}'; @override - String getFirefoxDownloadFilename(String version) => - 'Firefox ${version}.dmg'; + String getFirefoxDownloadFilename(String version) => 'Firefox ${version}.dmg'; @override String getFirefoxExecutablePath(io.Directory versionDir) => - path.join(versionDir.path, 'Firefox.app','Contents','MacOS', 'firefox'); + path.join(versionDir.path, 'Firefox.app', 'Contents', 'MacOS', 'firefox'); @override String getFirefoxLatestVersionUrl() => @@ -243,3 +250,14 @@ class DevNull implements StringSink { } bool get isCirrus => io.Platform.environment['CIRRUS_CI'] == 'true'; + +/// There might be proccesses started during the tests. +/// +/// Use this list to store those Processes, for cleaning up before shutdown. +final List processesToCleanUp = List(); + +/// There might be temporary directories created during the tests. +/// +/// Use this list to store those directories and for deleteing them before +/// shutdown. +final List temporaryDirectories = List(); diff --git a/lib/web_ui/dev/environment.dart b/lib/web_ui/dev/environment.dart index 35c11a9e073dc..6af6e6e874271 100644 --- a/lib/web_ui/dev/environment.dart +++ b/lib/web_ui/dev/environment.dart @@ -22,6 +22,7 @@ class Environment { final io.Directory hostDebugUnoptDir = io.Directory(pathlib.join(outDir.path, 'host_debug_unopt')); final io.Directory dartSdkDir = io.Directory(pathlib.join(hostDebugUnoptDir.path, 'dart-sdk')); final io.Directory webUiRootDir = io.Directory(pathlib.join(engineSrcDir.path, 'flutter', 'lib', 'web_ui')); + final io.Directory integrationTestsDir = io.Directory(pathlib.join(engineSrcDir.path, 'flutter', 'e2etests', 'web')); for (io.Directory expectedDirectory in [engineSrcDir, outDir, hostDebugUnoptDir, dartSdkDir, webUiRootDir]) { if (!expectedDirectory.existsSync()) { @@ -34,6 +35,7 @@ class Environment { self: self, webUiRootDir: webUiRootDir, engineSrcDir: engineSrcDir, + integrationTestsDir: integrationTestsDir, outDir: outDir, hostDebugUnoptDir: hostDebugUnoptDir, dartSdkDir: dartSdkDir, @@ -44,6 +46,7 @@ class Environment { this.self, this.webUiRootDir, this.engineSrcDir, + this.integrationTestsDir, this.outDir, this.hostDebugUnoptDir, this.dartSdkDir, @@ -58,6 +61,9 @@ class Environment { /// Path to the engine's "src" directory. final io.Directory engineSrcDir; + /// Path to the web integration tests. + final io.Directory integrationTestsDir; + /// Path to the engine's "out" directory. /// /// This is where you'll find the ninja output, such as the Dart SDK. diff --git a/lib/web_ui/dev/felt b/lib/web_ui/dev/felt index 07a71f37e8211..56b031ce0b616 100755 --- a/lib/web_ui/dev/felt +++ b/lib/web_ui/dev/felt @@ -56,9 +56,9 @@ install_deps() { KERNEL_NAME=`uname` if [[ $KERNEL_NAME == *"Darwin"* ]] then - echo "Running on MacOS. Increase the user limits" - ulimit -n 50000 - ulimit -u 4096 + echo "Running on MacOS. Increase the user limits" + ulimit -n 50000 + ulimit -u 4096 fi if [[ "$FELT_USE_SNAPSHOT" == "false" || "$FELT_USE_SNAPSHOT" == "0" ]]; then diff --git a/lib/web_ui/dev/felt.dart b/lib/web_ui/dev/felt.dart index d192431791b39..fd4d2f6c73645 100644 --- a/lib/web_ui/dev/felt.dart +++ b/lib/web_ui/dev/felt.dart @@ -9,6 +9,7 @@ import 'package:args/command_runner.dart'; import 'build.dart'; import 'clean.dart'; +import 'common.dart'; import 'licenses.dart'; import 'test_runner.dart'; @@ -41,12 +42,29 @@ void main(List args) async { io.exit(64); // Exit code 64 indicates a usage error. } catch (e) { rethrow; + } finally { + _cleanup(); } // Sometimes the Dart VM refuses to quit. io.exit(io.exitCode); } +void _cleanup() { + // Cleanup remaining processes if any. + if (processesToCleanUp.length > 0) { + for (io.Process process in processesToCleanUp) { + process.kill(); + } + } + // Delete temporary directories. + if (temporaryDirectories.length > 0) { + for (io.Directory directory in temporaryDirectories) { + directory.deleteSync(recursive: true); + } + } +} + void _listenToShutdownSignals() { io.ProcessSignal.sigint.watch().listen((_) { print('Received SIGINT. Shutting down.'); diff --git a/lib/web_ui/dev/integration_tests_manager.dart b/lib/web_ui/dev/integration_tests_manager.dart new file mode 100644 index 0000000000000..bd97ec878a7ef --- /dev/null +++ b/lib/web_ui/dev/integration_tests_manager.dart @@ -0,0 +1,350 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; +import 'package:path/path.dart' as pathlib; +import 'package:web_driver_installer/chrome_driver_installer.dart'; + +import 'common.dart'; +import 'environment.dart'; +import 'utils.dart'; + +class IntegrationTestsManager { + final String _browser; + + /// Installation directory for browser's driver. + /// + /// Always re-install since driver can change frequently. + /// It usually changes with each the browser version changes. + /// A better solution would be installing the browser and the driver at the + /// same time. + // TODO(nurhan): change the web installers to install driver and the browser + // at the same time. + final io.Directory _browserDriverDir; + + /// This is the parent directory for all drivers. + /// + /// This directory is saved to [temporaryDirectories] and deleted before + /// tests shutdown. + final io.Directory _drivers; + + IntegrationTestsManager(this._browser) + : this._browserDriverDir = io.Directory( + pathlib.join(environment.webUiRootDir.path, 'drivers', _browser)), + this._drivers = io.Directory( + pathlib.join(environment.webUiRootDir.path, 'drivers')); + + Future runTests() async { + if (_browser != 'chrome') { + print('WARNING: integration tests are only supported on chrome for now'); + return false; + } else { + await prepareDriver(); + // TODO(nurhan): https://github.com/flutter/flutter/issues/52987 + return await _runTests(); + } + } + + void _cloneWebInstallers() async { + final int exitCode = await runProcess( + 'git', + [ + 'clone', + 'https://github.com/flutter/web_installers.git', + ], + workingDirectory: _browserDriverDir.path, + ); + + if (exitCode != 0) { + io.stderr.writeln('ERROR: ' + 'Failed to clone web installers. Exited with exit code $exitCode'); + throw DriverException('ERROR: ' + 'Failed to clone web installers. Exited with exit code $exitCode'); + } + } + + Future _runPubGet(String workingDirectory) async { + final String executable = isCirrus ? environment.pubExecutable : 'flutter'; + final List arguments = isCirrus + ? [ + 'get', + ] + : [ + 'pub', + 'get', + ]; + final int exitCode = await runProcess( + executable, + arguments, + workingDirectory: workingDirectory, + ); + + if (exitCode != 0) { + io.stderr.writeln( + 'ERROR: Failed to run pub get. Exited with exit code $exitCode'); + return false; + } else { + return true; + } + } + + void _runDriver() async { + final int exitCode = await runProcess( + environment.dartExecutable, + [ + 'lib/web_driver_installer.dart', + '${_browser}driver', + '--install-only', + ], + workingDirectory: pathlib.join( + _browserDriverDir.path, 'web_installers', 'packages', 'web_drivers'), + ); + + if (exitCode != 0) { + io.stderr.writeln( + 'ERROR: Failed to run driver. Exited with exit code $exitCode'); + throw DriverException( + 'ERROR: Failed to run driver. Exited with exit code $exitCode'); + } + startProcess( + './chromedriver/chromedriver', + ['--port=4444'], + workingDirectory: pathlib.join( + _browserDriverDir.path, 'web_installers', 'packages', 'web_drivers'), + ); + print('INFO: Driver started'); + } + + void prepareDriver() async { + final io.Directory priorCurrentDirectory = io.Directory.current; + if (_browserDriverDir.existsSync()) { + _browserDriverDir.deleteSync(recursive: true); + } + + _browserDriverDir.createSync(recursive: true); + temporaryDirectories.add(_drivers); + + // TODO(nurhan): We currently need git clone for getting the driver lock + // file. Remove this after making changes in web_installers. + await _cloneWebInstallers(); + // Change the directory to the driver_lock.yaml file's directory. + io.Directory.current = pathlib.join( + _browserDriverDir.path, 'web_installers', 'packages', 'web_drivers'); + // Chrome is the only browser supporting integration tests for now. + ChromeDriverInstaller chromeDriverInstaller = ChromeDriverInstaller(); + bool installation = await chromeDriverInstaller.install(); + + if (installation) { + io.Directory.current = priorCurrentDirectory; + await _runDriver(); + } else { + throw DriverException('ERROR: Installing driver failed'); + } + } + + /// Runs all the web tests under e2e_tests/web. + Future _runTests() async { + // Only list the files under e2e_tests/web. + final List entities = + environment.integrationTestsDir.listSync(followLinks: false); + + bool allTestsPassed = true; + for (io.FileSystemEntity e in entities) { + // The tests should be under this directories. + if (e is io.Directory) { + allTestsPassed = allTestsPassed && await _validateAndRunTests(e); + } + } + return allTestsPassed; + } + + /// Run tests in a single directory under: e2e_tests/web. + /// + /// Run `flutter pub get` as the first step. + /// + /// Validate the directory before running the tests. Each directory is + /// expected to be a test project which includes a `pubspec.yaml` file + /// and a `test_driver` directory. + Future _validateAndRunTests(io.Directory directory) async { + _validateTestDirectory(directory); + await _runPubGet(directory.path); + final bool testResults = await _runTestsInDirectory(directory); + return testResults; + } + + Future _runTestsInDirectory(io.Directory directory) async { + final io.Directory testDirectory = + io.Directory(pathlib.join(directory.path, 'test_driver')); + final List entities = testDirectory + .listSync(followLinks: false) + .whereType() + .toList(); + + final List e2eTestsToRun = List(); + + // The following loops over the contents of the directory and saves an + // expected driver file name for each e2e test assuming any dart file + // not ending with `_test.dart` is an e2e test. + // Other files are not considered since developers can add files such as + // README. + for (io.File f in entities) { + final String basename = pathlib.basename(f.path); + if (!basename.contains('_test.dart') && basename.endsWith('.dart')) { + e2eTestsToRun.add(basename); + } + } + + print( + 'INFO: In project ${directory} ${e2eTestsToRun.length} tests to run.'); + + int numberOfPassedTests = 0; + int numberOfFailedTests = 0; + for (String fileName in e2eTestsToRun) { + final bool testResults = + await _runTestsInProfileMode(directory, fileName); + if (testResults) { + numberOfPassedTests++; + } else { + numberOfFailedTests++; + } + } + final int numberOfTestsRun = numberOfPassedTests + numberOfFailedTests; + + print('INFO: ${numberOfTestsRun} tests run. ${numberOfPassedTests} passed ' + 'and ${numberOfFailedTests} failed.'); + return numberOfFailedTests == 0; + } + + Future _runTestsInProfileMode( + io.Directory directory, String testName) async { + final int exitCode = await runProcess( + 'flutter', + [ + 'drive', + '--target=test_driver/${testName}', + '-d', + 'web-server', + '--profile', + '--browser-name=$_browser', + '--local-engine=host_debug_unopt', + ], + workingDirectory: directory.path, + ); + + if (exitCode != 0) { + final String statementToRun = 'flutter drive ' + '--target=test_driver/${testName} -d web-server --profile ' + '--browser-name=$_browser --local-engine=host_debug_unopt'; + io.stderr + .writeln('ERROR: Failed to run test. Exited with exit code $exitCode' + '. Statement to run $testName locally use the following ' + 'command:\n\n$statementToRun'); + return false; + } else { + return true; + } + } + + /// Validate the directory has a `pubspec.yaml` file and a `test_driver` + /// directory. + /// + /// Also check the validity of files under `test_driver` directory calling + /// [_checkE2ETestsValidity] method. + void _validateTestDirectory(io.Directory directory) { + final List entities = + directory.listSync(followLinks: false); + + // Whether the project has the pubspec.yaml file. + bool pubSpecFound = false; + // The test directory 'test_driver'. + io.Directory testDirectory = null; + + for (io.FileSystemEntity e in entities) { + // The tests should be under this directories. + final String baseName = pathlib.basename(e.path); + if (e is io.Directory && baseName == 'test_driver') { + testDirectory = e; + } + if (e is io.File && baseName == 'pubspec.yaml') { + pubSpecFound = true; + } + } + if (!pubSpecFound) { + throw StateError('ERROR: pubspec.yaml file not found in the test project ' + 'in the directory ${directory.path}.'); + } + if (testDirectory == null) { + throw StateError( + 'ERROR: test_driver folder not found in the test project.' + 'in the directory ${directory.path}.'); + } else { + _checkE2ETestsValidity(testDirectory); + } + } + + /// Checks if each e2e test file in the directory has a driver test + /// file to run it. + /// + /// Prints informative message to the developer if an error has found. + /// For each e2e test which has name {name}.dart there will be a driver + /// file which drives it. The driver file should be named: + /// {name}_test.dart + void _checkE2ETestsValidity(io.Directory testDirectory) { + final Iterable directories = + testDirectory.listSync(followLinks: false).whereType(); + + if (directories.length > 0) { + throw StateError('${testDirectory.path} directory should not contain ' + 'any sub-directories'); + } + + final Iterable entities = + testDirectory.listSync(followLinks: false).whereType(); + + final Set expectedDriverFileNames = Set(); + final Set foundDriverFileNames = Set(); + int numberOfTests = 0; + + // The following loops over the contents of the directory and saves an + // expected driver file name for each e2e test assuming any file + // not ending with `_test.dart` is an e2e test. + for (io.File f in entities) { + final String basename = pathlib.basename(f.path); + if (basename.contains('_test.dart')) { + // First remove this from expectedSet if not there add to the foundSet. + if (!expectedDriverFileNames.remove(basename)) { + foundDriverFileNames.add(basename); + } + } else if (basename.contains('.dart')) { + // Only run on dart files. + final String e2efileName = pathlib.basenameWithoutExtension(f.path); + final String expectedDriverName = '${e2efileName}_test.dart'; + numberOfTests++; + // First remove this from foundSet if not there add to the expectedSet. + if (!foundDriverFileNames.remove(expectedDriverName)) { + expectedDriverFileNames.add(expectedDriverName); + } + } + } + + if (numberOfTests == 0) { + throw StateError( + 'WARNING: No tests to run in this directory ${testDirectory.path}'); + } + + // TODO(nurhan): In order to reduce the work required from team members, + // remove the need for driver file, by using the same template file. + // Some driver files are missing. + if (expectedDriverFileNames.length > 0) { + for (String expectedDriverName in expectedDriverFileNames) { + print('ERROR: Test driver file named has ${expectedDriverName} ' + 'not found under directory ${testDirectory.path}. Stopping the ' + 'integration tests. Please add ${expectedDriverName}. Check to ' + 'README file on more details on how to setup integration tests.'); + } + throw StateError('Error in test files. Check the logs for ' + 'further instructions'); + } + } +} diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 0779ad450fad3..fa66b05954d3c 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -15,11 +15,24 @@ import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_im import 'package:test_core/src/executable.dart' as test; // ignore: implementation_imports +import 'integration_tests_manager.dart'; import 'supported_browsers.dart'; import 'test_platform.dart'; import 'environment.dart'; import 'utils.dart'; +/// The type of tests requested by the tool user. +enum TestTypesRequested { + /// For running the unit tests only. + unit, + + /// For running the integration tests only. + integration, + + /// For running both unit and integration tests. + all, +} + class TestCommand extends Command { TestCommand() { argParser @@ -29,6 +42,19 @@ class TestCommand extends Command { 'opportunity to add breakpoints or inspect loaded code before ' 'running the code.', ) + ..addFlag( + 'unit-tests-only', + defaultsTo: false, + help: 'felt test command runs the unit tests and the integration tests ' + 'at the same time. If this flag is set, only run the unit tests.', + ) + ..addFlag( + 'integration-tests-only', + defaultsTo: false, + help: 'felt test command runs the unit tests and the integration tests ' + 'at the same time. If this flag is set, only run the integration ' + 'tests.', + ) ..addFlag( 'update-screenshot-goldens', defaultsTo: false, @@ -54,11 +80,62 @@ class TestCommand extends Command { @override final String description = 'Run tests.'; + TestTypesRequested testTypesRequested = null; + + /// Check the flags to see what type of tests are requested. + TestTypesRequested findTestType() { + if (argResults['unit-tests-only'] && argResults['integration-tests-only']) { + throw ArgumentError('Conflicting arguments: unit-tests-only and ' + 'integration-tests-only are both set'); + } else if (argResults['unit-tests-only']) { + print('Running the unit tests only'); + return TestTypesRequested.unit; + } else if (argResults['integration-tests-only']) { + if (!isChrome) { + throw UnimplementedError( + 'Integration tests are only available on Chrome Desktop for now'); + } + return TestTypesRequested.integration; + } else { + return TestTypesRequested.all; + } + } + @override Future run() async { SupportedBrowsers.instance ..argParsers.forEach((t) => t.parseOptions(argResults)); + // Check the flags to see what type of integration tests are requested. + testTypesRequested = findTestType(); + + switch (testTypesRequested) { + case TestTypesRequested.unit: + return runUnitTests(); + case TestTypesRequested.integration: + return runIntegrationTests(); + case TestTypesRequested.all: + bool integrationTestResult = await runIntegrationTests(); + bool unitTestResult = await runUnitTests(); + if (integrationTestResult != unitTestResult) { + print('Tests run. Integration tests passed: $integrationTestResult ' + 'unit tests passed: $unitTestResult'); + } + return integrationTestResult && unitTestResult; + } + return false; + } + + Future runIntegrationTests() async { + // TODO(nurhan): https://github.com/flutter/flutter/issues/52983 + if (io.Platform.environment['LUCI_CONTEXT'] != null || isCirrus) { + return true; + } + + return IntegrationTestsManager(browser).runTests(); + } + + Future runUnitTests() async { _copyTestFontsIntoWebUi(); await _buildHostPage(); if (io.Platform.isWindows) { @@ -252,18 +329,18 @@ class TestCommand extends Command { Future _buildTests({List targets}) async { List arguments = [ - 'run', - 'build_runner', - 'build', - 'test', - '-o', - 'build', - if (targets != null) - for (FilePath path in targets) ...[ - '--build-filter=${path.relativeToWebUi}.js', - '--build-filter=${path.relativeToWebUi}.browser_test.dart.js', - ], - ]; + 'run', + 'build_runner', + 'build', + 'test', + '-o', + 'build', + if (targets != null) + for (FilePath path in targets) ...[ + '--build-filter=${path.relativeToWebUi}.js', + '--build-filter=${path.relativeToWebUi}.browser_test.dart.js', + ], + ]; final int exitCode = await runProcess( environment.pubExecutable, arguments, diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index d60df326b3eea..5b7e6f306d9a5 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -9,6 +9,7 @@ import 'dart:io' as io; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; +import 'common.dart'; import 'environment.dart'; class FilePath { @@ -62,6 +63,25 @@ Future runProcess( return exitCode; } +/// Runs [executable]. Do not follow the exit code or the output. +void startProcess( + String executable, + List arguments, { + String workingDirectory, + bool mustSucceed: false, +}) async { + final io.Process process = await io.Process.start( + executable, + arguments, + workingDirectory: workingDirectory, + // Running the process in a system shell for Windows. Otherwise + // the process is not able to get Dart from path. + runInShell: io.Platform.isWindows, + mode: io.ProcessStartMode.inheritStdio, + ); + processesToCleanUp.add(process); +} + /// Runs [executable] and returns its standard output as a string. /// /// If the process fails, throws a [ProcessException]. diff --git a/lib/web_ui/pubspec.yaml b/lib/web_ui/pubspec.yaml index 12e7d8d8f7d7a..997e119224c72 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -21,3 +21,8 @@ dev_dependencies: watcher: 0.9.7+12 web_engine_tester: path: ../../web_sdk/web_engine_tester + web_driver_installer: + git: + url: git://github.com/flutter/web_installers.git + path: packages/web_drivers/ + ref: dae38d8839cc39f997fb4229f1382680b8758b4f From 2f769be66344df0891120b5cd25b91ba30a37f3e Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 20 Mar 2020 19:50:00 -0700 Subject: [PATCH 076/386] Soften shadows (#17231) * Soften shadows * update goldens --- lib/web_ui/dev/goldens_lock.yaml | 2 +- lib/web_ui/lib/src/engine/canvas_pool.dart | 7 ++++++- lib/web_ui/lib/src/engine/shadow.dart | 4 +++- lib/web_ui/lib/src/engine/util.dart | 10 ++++++++++ .../test/golden_tests/engine/shadow_golden_test.dart | 2 +- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index 1b73145f48a3b..f32239505dc7a 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: 8f692819e8881b7d2131dbd61d965c21d5e3e345 +revision: ae6003206eb721137c20cd56d8d1d8e2a76d6dd1 diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index b5119afb82c10..3702513e261a2 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -613,7 +613,12 @@ class _CanvasPool extends _SaveStackTracking { context.save(); context.filter = 'none'; context.strokeStyle = ''; - context.fillStyle = colorToCssString(color); + final int red = color.red; + final int green = color.green; + final int blue = color.blue; + // Multiply by 0.4 to make shadows less aggressive (https://github.com/flutter/flutter/issues/52734) + final int alpha = (0.4 * color.alpha).round(); + context.fillStyle = colorComponentsToCssString(red, green, blue, alpha); context.shadowBlur = shadow.blurWidth; context.shadowColor = colorToCssString(color.withAlpha(0xff)); context.shadowOffsetX = shadow.offset.dx; diff --git a/lib/web_ui/lib/src/engine/shadow.dart b/lib/web_ui/lib/src/engine/shadow.dart index 3c46dac7681b5..62806e029b518 100644 --- a/lib/web_ui/lib/src/engine/shadow.dart +++ b/lib/web_ui/lib/src/engine/shadow.dart @@ -133,7 +133,9 @@ void applyCssShadow( if (shadow == null) { element.style.boxShadow = 'none'; } else { + // Multiply by 0.4 to make shadows less aggressive (https://github.com/flutter/flutter/issues/52734) + final double alpha = 0.4 * color.alpha / 255; element.style.boxShadow = '${shadow.offset.dx}px ${shadow.offset.dy}px ' - '${shadow.blurWidth}px 0px rgb(${color.red}, ${color.green}, ${color.blue})'; + '${shadow.blurWidth}px 0px rgba(${color.red}, ${color.green}, ${color.blue}, $alpha)'; } } diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index e0d69b68bfbaa..54a03397de936 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -349,6 +349,16 @@ String _colorToCssStringRgbOnly(ui.Color color) { return '#${paddedValue.substring(paddedValue.length - 6)}'; } +/// Converts color components to a CSS compatible attribute value. +String colorComponentsToCssString(int r, int g, int b, int a) { + if (a == 255) { + return 'rgb($r,$g,$b)'; + } else { + final double alphaRatio = a / 255; + return 'rgba($r,$g,$b,${alphaRatio.toStringAsFixed(2)})'; + } +} + /// Determines if the (dynamic) exception passed in is a NS_ERROR_FAILURE /// (from Firefox). /// diff --git a/lib/web_ui/test/golden_tests/engine/shadow_golden_test.dart b/lib/web_ui/test/golden_tests/engine/shadow_golden_test.dart index d5f3dc8388e7c..35651a42276e8 100644 --- a/lib/web_ui/test/golden_tests/engine/shadow_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/shadow_golden_test.dart @@ -12,7 +12,7 @@ import 'package:web_engine_tester/golden_tester.dart'; import 'scuba.dart'; -const Color _kShadowColor = Color.fromARGB(255, 255, 0, 0); +const Color _kShadowColor = Color.fromARGB(255, 0, 0, 0); void main() async { final Rect region = Rect.fromLTWH(0, 0, 550, 300); From cafa097f9236328345ffa4e1b46abe835afbc032 Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 20 Mar 2020 20:20:53 -0700 Subject: [PATCH 077/386] Revert "Roll src/third_party/dart ba8baa46b452..fbd0c8a46813 (35 commits) (#17241)" (#17243) This reverts commit 047c03bf5cc57ab2d4d1025993b1dbc1b83a78a9. This roll also breaks web tests with: ``` org-dartlang-app:///test/material/icon_button_test.dart:15:7: Warning: Implementing 'Function' is deprecated. Try removing 'Function' from the 'implements' clause. class MockOnPressedFunction implements Function { ``` --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 254a7b124fc96..3d204b33e9132 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'fbd0c8a468136ae4e88d54f2f7bb0e830393591b', + 'dart_revision': 'ba8baa46b4527a631c077f5a59c348931688d91a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -77,7 +77,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '11afdac99fe67373829f37acc75b625011d360ad', + 'dart_pub_rev': '429a06039d185149f387a65e3503b0693ce6d24e', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1e48310747223..4ccdf267b2ce3 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: b2aca8df422a04ebeb57d71809616e27 +Signature: bb7475da375e6d3d09b02525bab95d8b UNUSED LICENSES: From 3dd688c5fb2177a664c8a1c92c888f5dd2e194b7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 21 Mar 2020 02:51:02 -0400 Subject: [PATCH 078/386] Roll src/third_party/dart ba8baa46b452..82f8a5451fae (40 commits) (#17244) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 3d204b33e9132..b33b0d13cf416 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'ba8baa46b4527a631c077f5a59c348931688d91a', + 'dart_revision': '82f8a5451fae3334fdab7a84ed269f831c168276', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -77,7 +77,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '429a06039d185149f387a65e3503b0693ce6d24e', + 'dart_pub_rev': '11afdac99fe67373829f37acc75b625011d360ad', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 4ccdf267b2ce3..1ec6ad0a25281 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: bb7475da375e6d3d09b02525bab95d8b +Signature: 1c27e10f486057edc8e288aa560f6031 UNUSED LICENSES: From 7b9a6783231f87488e46185b7142c7dba43cf8df Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 21 Mar 2020 07:26:02 -0400 Subject: [PATCH 079/386] Roll src/third_party/dart 82f8a5451fae..f247613737ec (3 commits) (#17245) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index b33b0d13cf416..4d97c8ade680f 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '82f8a5451fae3334fdab7a84ed269f831c168276', + 'dart_revision': 'f247613737ecfcba9d1a699b29c15c234340d65e', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 31c47278ba63f3986c6d6d6caed1a1a649f03b77 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sat, 21 Mar 2020 22:12:54 -0700 Subject: [PATCH 080/386] Document flutter::RuntimeController. (#17250) --- runtime/runtime_controller.h | 354 ++++++++++++++++++++++++++++++++++- 1 file changed, 353 insertions(+), 1 deletion(-) diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index cea325bad7444..4d91dfabf1bd4 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -22,13 +22,89 @@ #include "rapidjson/stringbuffer.h" namespace flutter { + class Scene; class RuntimeDelegate; class View; class Window; +//------------------------------------------------------------------------------ +/// Represents an instance of a running root isolate with window bindings. In +/// normal operation, a single instance of this object is owned by the engine +/// per shell. This object may only be created, used, and collected on the UI +/// task runner. Window state queried by the root isolate is stored by this +/// object. In cold-restart scenarios, the engine may collect this collect +/// before installing a new runtime controller in its place. The Clone method +/// may be used by the engine to copy the currently accumulated window state so +/// it can be referenced by the new runtime controller. +/// class RuntimeController final : public WindowClient { public: + //---------------------------------------------------------------------------- + /// @brief Creates a new instance of a runtime controller. This is + /// usually only done by the engine instance associated with the + /// shell. + /// + /// @param client The runtime delegate. This is + /// usually the `Engine` instance. + /// @param vm A reference to a running Dart VM. + /// The runtime controller must be + /// collected before the VM is + /// destroyed (this order is + /// guaranteed by the shell). + /// @param[in] isolate_snapshot The isolate snapshot used to start + /// the root isolate managed by this + /// runtime controller. The isolate + /// must be transitioned into the + /// running phase manually by the + /// caller. + /// @param[in] task_runners The task runners used by the shell + /// hosting this runtime controller. + /// This may be used by the isolate to + /// scheduled asynchronous texture + /// uploads or post tasks to the + /// platform task runner. + /// @param[in] snapshot_delegate The snapshot delegate used by the + /// isolate to gather raster snapshots + /// of Flutter view hierarchies. + /// @param[in] io_manager The IO manager used by the isolate + /// for asynchronous texture uploads. + /// @param[in] unref_queue The unref queue used by the + /// isolate to collect resources that + /// may reference resources on the + /// GPU. + /// @param[in] image_decoder The image decoder + /// @param[in] advisory_script_uri The advisory script URI (only used + /// for debugging). This does not + /// affect the code being run in the + /// isolate in any way. + /// @param[in] advisory_script_entrypoint The advisory script entrypoint + /// (only used for debugging). This + /// does not affect the code being run + /// in the isolate in any way. The + /// isolate must be transitioned to + /// the running state explicitly by + /// the caller. + /// @param[in] idle_notification_callback The idle notification callback. + /// This allows callers to run native + /// code in isolate scope when the VM + /// is about to be notified that the + /// engine is going to be idle. + /// @param[in] window_data The window data (if exists). + /// @param[in] isolate_create_callback The isolate create callback. This + /// allows callers to run native code + /// in isolate scope on the UI task + /// runner as soon as the root isolate + /// has been created. + /// @param[in] isolate_shutdown_callback The isolate shutdown callback. + /// This allows callers to run native + /// code in isolate scoped on the UI + /// task runner just as the root + /// isolate is about to be torn down. + /// @param[in] persistent_isolate_data Unstructured persistent read-only + /// data that the root isolate can + /// access in a synchronous manner. + /// RuntimeController( RuntimeDelegate& client, DartVM* vm, @@ -41,53 +117,329 @@ class RuntimeController final : public WindowClient { std::string advisory_script_uri, std::string advisory_script_entrypoint, const std::function& idle_notification_callback, - const WindowData& data, + const WindowData& window_data, const fml::closure& isolate_create_callback, const fml::closure& isolate_shutdown_callback, std::shared_ptr persistent_isolate_data); + // |WindowClient| ~RuntimeController() override; + //---------------------------------------------------------------------------- + /// @brief Clone the the runtime controller. This re-creates the root + /// isolate with the same snapshots and copies all window data to + /// the new instance. This is usually only used in the debug + /// runtime mode to support the cold-restart scenario. + /// + /// @return A clone of the existing runtime controller. + /// std::unique_ptr Clone() const; + //---------------------------------------------------------------------------- + /// @brief Forward the specified window metrics to the running isolate. + /// If the isolate is not running, these metrics will be saved and + /// flushed to the isolate when it starts. + /// + /// @param[in] metrics The metrics. + /// + /// @return If the window metrics were forwarded to the running isolate. + /// bool SetViewportMetrics(const ViewportMetrics& metrics); + //---------------------------------------------------------------------------- + /// @brief Forward the specified locale data to the running isolate. If + /// the isolate is not running, this data will be saved and + /// flushed to the isolate when it starts running. + /// + /// @deprecated The persistent isolate data must be used for this purpose + /// instead. + /// + /// @param[in] locale_data The locale data + /// + /// @return If the locale data was forwarded to the running isolate. + /// bool SetLocales(const std::vector& locale_data); + //---------------------------------------------------------------------------- + /// @brief Forward the user settings data to the running isolate. If the + /// isolate is not running, this data will be saved and flushed to + /// the isolate when it starts running. + /// + /// @deprecated The persistent isolate data must be used for this purpose + /// instead. + /// + /// @param[in] data The user settings data. + /// + /// @return If the user settings data was forwarded to the running + /// isolate. + /// bool SetUserSettingsData(const std::string& data); + //---------------------------------------------------------------------------- + /// @brief Forward the lifecycle state data to the running isolate. If + /// the isolate is not running, this data will be saved and + /// flushed to the isolate when it starts running. + /// + /// @deprecated The persistent isolate data must be used for this purpose + /// instead. + /// + /// @param[in] data The lifecycle state data. + /// + /// @return If the lifecycle state data was forwarded to the running + /// isolate. + /// bool SetLifecycleState(const std::string& data); + //---------------------------------------------------------------------------- + /// @brief Notifies the running isolate about whether the semantics tree + /// should be generated or not. If the isolate is not running, + /// this preference will be saved and flushed to the isolate when + /// it starts running. + /// + /// @param[in] enabled Indicates whether to generate the semantics tree. + /// + /// @return If the semantics tree generation preference was forwarded to + /// the running isolate. + /// bool SetSemanticsEnabled(bool enabled); + //---------------------------------------------------------------------------- + /// @brief Forward the preference of accessibility features that must be + /// enabled in the semantics tree to the running isolate. If the + /// isolate is not running, this data will be saved and flushed to + /// the isolate when it starts running. + /// + /// @param[in] flags The accessibility features that must be generated in + /// the semantics tree. + /// + /// @return If the preference of accessibility features was forwarded to + /// the running isolate. + /// bool SetAccessibilityFeatures(int32_t flags); + //---------------------------------------------------------------------------- + /// @brief Notifies the running isolate that it should start generating a + /// new frame. + /// + /// @see `Engine::BeginFrame` for more context. + /// + /// @param[in] frame_time The point at which the current frame interval + /// began. May be used by animation interpolators, + /// physics simulations, etc.. + /// + /// @return If notification to begin frame rendering was delivered to the + /// running isolate. + /// bool BeginFrame(fml::TimePoint frame_time); + //---------------------------------------------------------------------------- + /// @brief Dart code cannot fully measure the time it takes for a + /// specific frame to be rendered. This is because Dart code only + /// runs on the UI task runner. That is only a small part of the + /// overall frame workload. The GPU task runner frame workload is + /// executed on a thread where Dart code cannot run (and hence + /// instrument). Besides, due to the pipelined nature of rendering + /// in Flutter, there may be multiple frame workloads being + /// processed at any given time. However, for non-Timeline based + /// profiling, it is useful for trace collection and processing to + /// happen in Dart. To do this, the GPU task runner frame + /// workloads need to be instrumented separately. After a set + /// number of these profiles have been gathered, they need to be + /// reported back to Dart code. The engine reports this extra + /// instrumentation information back to Dart code running on the + /// engine by invoking this method at predefined intervals. + /// + /// @see `Engine::ReportTimings`, `FrameTiming` + /// + /// @param[in] timings Collection of `FrameTiming::kCount` * `n` timestamps + /// for `n` frames whose timings have not been reported + /// yet. A collection of integers is reported here for + /// easier conversions to Dart objects. The timestamps + /// are measured against the system monotonic clock + /// measured in microseconds. + /// bool ReportTimings(std::vector timings); + //---------------------------------------------------------------------------- + /// @brief Notify the Dart VM that no frame workloads are expected on the + /// UI task runner till the specified deadline. The VM uses this + /// opportunity to perform garbage collection operations is a + /// manner that interferes as little as possible with frame + /// rendering. + /// + /// NotifyIdle is advisory and not making the same or making it with + /// insufficient deadlines does not mean that the VM will keep consuming + /// memory indefinitely. Even if the engine made absolutely no calls to + /// NotifyIdle, the VM would still perform garbage collection. The only issue + /// in such a world would be that the GC pause would happen in the middle of a + /// frame workload. + /// + /// The garbage collection mechanism and its thresholds are internal + /// implementation details and absolutely no guarantees are made about the + /// threshold discussed below. This discussion is also an oversimplification + /// but hopefully serves to calibrate expectations about GC behavior: + /// * When the Dart VM and its root isolate are initialized, the memory + /// consumed upto that point are treated as a baseline. + /// * A fixed percentage of the memory consumed (~20%) over the baseline is + /// treated as the hard threshold. + /// * The memory in play is divided into old space and new space. The new + /// space is typically very small and fills up rapidly. + /// * The baseline plus the threshold is considered the old space while the + /// small new space is a separate region (typically a few pages). + /// * The total old space size minus the max new space size is treated as the + /// soft threshold. + /// * In a world where there is no call to NotifyIdle, when the total + /// allocation exceeds the soft threshold, a concurrent mark is initiated in + /// the VM. There is a “small” pause that occurs when the concurrent mark is + /// initiated and another pause when the mark concludes and a sweep is + /// initiated. + /// * If the total allocations exceeds the the hard threshold, a “big” + /// stop-the-world pause is initiated. + /// * If after either the sweep after the concurrent mark, or, the + /// stop-the-world pause, the consumption returns to be below the soft + /// threshold, the dance begins anew. + /// * If after both the “small” and “big” pauses, memory usage is still over + /// the hard threshold, i.e, the objects are still reachable, that amount of + /// memory is treated as the new baseline and a fixed percentage of the new + /// baseline over the new baseline is now the new hard threshold. + /// * Updating the baseline will continue till memory for the updated old + /// space can be allocated from the operating system. These allocations will + /// typically fail due to address space exhaustion on 32-bit systems and + /// page table exhaustion on 64-bit systems. + /// * NotifyIdle initiates the concurrent mark preemptively. The deadline is + /// used by the VM to determine if the corresponding sweep can be performed + /// within the deadline. This way, jank due to “small” pauses can be + /// ameliorated. + /// * There is no ability to stop a “big” pause on reaching the hard threshold + /// in the old space. The best you can do is release (by making them + /// unreachable) objects eagerly so that the are marked as unreachable in + /// the concurrent mark initiated by either reaching the soft threshold or + /// an explicit NotifyIdle. + /// * If you are running out of memory, its because too many large objects + /// were allocation and remained reachable such that the the old space kept + /// growing till it could grow no more. + /// * At the edges of allocation thresholds, failures can occur gracefully if + /// the instigating allocation was made in the Dart VM or rather gracelessly + /// if the allocation is made by some native component. + /// + /// @see `Dart_TimelineGetMicros` + /// + /// @bug The `deadline` argument must be converted to `std::chrono` + /// instead of a raw integer. + /// + /// @param[in] deadline The deadline measures in microseconds against the + /// system's monotonic time. The clock can be accessed via + /// `Dart_TimelineGetMicros`. + /// + /// @return If the idle notification was forwarded to the running isolate. + /// bool NotifyIdle(int64_t deadline); + //---------------------------------------------------------------------------- + /// @brief Returns if the root isolate is running. The isolate must be + /// transitioned to the running phase manually. The isolate can + /// stop running if it terminates execution on its own. + /// + /// @return True if root isolate running, False otherwise. + /// bool IsRootIsolateRunning() const; + //---------------------------------------------------------------------------- + /// @brief Dispatch the specified platform message to running root + /// isolate. + /// + /// @param[in] message The message to dispatch to the isolate. + /// + /// @return If the message was dispatched to the running root isolate. + /// This may fail is an isolate is not running. + /// bool DispatchPlatformMessage(fml::RefPtr message); + //---------------------------------------------------------------------------- + /// @brief Dispatch the specified pointer data message to the running + /// root isolate. + /// + /// @param[in] packet The pointer data message to dispatch to the isolate. + /// + /// @return If the pointer data message was dispatched. This may fail is + /// an isolate is not running. + /// bool DispatchPointerDataPacket(const PointerDataPacket& packet); + //---------------------------------------------------------------------------- + /// @brief Dispatch the semantics action to the specified accessibility + /// node. + /// + /// @param[in] id The identified of the accessibility node. + /// @param[in] action The semantics action to perform on the specified + /// accessibility node. + /// @param[in] args Optional data that applies to the specified action. + /// + /// @return If the semantics action was dispatched. This may fail if an + /// isolate is not running. + /// bool DispatchSemanticsAction(int32_t id, SemanticsAction action, std::vector args); + //---------------------------------------------------------------------------- + /// @brief Gets the main port identifier of the root isolate. + /// + /// @return The main port identifier. If no root isolate is running, + /// returns `ILLEGAL_PORT`. + /// Dart_Port GetMainPort(); + //---------------------------------------------------------------------------- + /// @brief Gets the debug name of the root isolate. But default, the + /// debug name of the isolate is derived from its advisory script + /// URI, advisory main entrypoint and its main port name. For + /// example, "main.dart$main-1234" where the script URI is + /// "main.dart", the entrypoint is "main" and the port name + /// "1234". Once launched, the isolate may re-christen itself + /// using a name it selects via `setIsolateDebugName` in + /// `window.dart`. This name is purely advisory and only used by + /// instrumentation and reporting purposes. + /// + /// @return The debug name of the root isolate. + /// std::string GetIsolateName(); + //---------------------------------------------------------------------------- + /// @brief Returns if the root isolate has any live receive ports. + /// + /// @return True if there are live receive ports, False otherwise. Return + /// False is the root isolate is not running as well. + /// bool HasLivePorts(); + //---------------------------------------------------------------------------- + /// @brief Get the last error encountered by the microtask queue. + /// + /// @return The last error encountered by the microtask queue. + /// tonic::DartErrorHandleType GetLastError(); + //---------------------------------------------------------------------------- + /// @brief Get a weak pointer to the root Dart isolate. This isolate may + /// only be locked on the UI task runner. Callers use this + /// accessor to transition to the root isolate to the running + /// phase. + /// + /// @return The root isolate reference. + /// std::weak_ptr GetRootIsolate(); + //---------------------------------------------------------------------------- + /// @brief Get the return code specified by the root isolate (if one is + /// present). + /// + /// @bug Change this method to return `std::optional` + /// instead. + /// + /// @return The root isolate return code. The first argument in the pair + /// indicates if one is specified by the root isolate. + /// std::pair GetRootIsolateReturnCode(); private: From f5314eaee92dba1d1e9bff16f8051fe587028417 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 01:31:03 -0400 Subject: [PATCH 081/386] Roll fuchsia/sdk/core/mac-amd64 from uyg4y... to nmCXC... (#17246) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 4d97c8ade680f..c32dd6aaf597e 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'uyg4yfQaJJ5IxN824uYTcsiT7_Rk2UX_EGN1dJ9NJ0oC' + 'version': 'nmCXCpUyObjvw-oyDUvCc07L3WmQYlOWC-XSBAfGMF0C' } ], 'condition': 'host_os == "mac"', From db02fa1952f8c4ec881728bd7b25d08f5411a945 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 01:36:04 -0400 Subject: [PATCH 082/386] Roll fuchsia/sdk/core/linux-amd64 from pTJOm... to kGu6P... (#17247) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c32dd6aaf597e..755b69880664e 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'pTJOmnMiJ9nHE2-CdHz0kzE6OgLnT5ANiLVR--vNFvYC' + 'version': 'kGu6P9EmEzY73hvUI6dqHTOmc_HFLa2x5EyBRrerwc4C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 6cb6d8f82516e..f1ea2e0074b09 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 08e886dba87b26ec51aea03fe463fec9 +Signature: 2328197eb79c8fd85e24500b21e9ed30 UNUSED LICENSES: From 7d21f97009cae691ba32e7cb451c28c2e39bbe60 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 01:41:02 -0400 Subject: [PATCH 083/386] Roll src/third_party/dart f247613737ec..861708483217 (1 commits) (#17248) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 755b69880664e..42f394580ed62 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'f247613737ecfcba9d1a699b29c15c234340d65e', + 'dart_revision': '861708483217c9c1ae2d467d647c67cd419bd064', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 59406cc7e911b93fa9f60df4230517bd7146ec09 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 01:46:04 -0400 Subject: [PATCH 084/386] Roll src/third_party/skia e41fa2d5e44d..cf3594b60362 (1 commits) (#17249) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 42f394580ed62..cf059af47ced0 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e41fa2d5e44d4615948bd769fef400b5017f5a61', + 'skia_revision': 'cf3594b60362996ce18dc40bd39c7de77e4ceaa7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2badbc58b2ba7..d110c0a9b65aa 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1d64f22b1df81102dce82042616fc41d +Signature: 705befc08806b2ed6990af599f1dddd7 UNUSED LICENSES: From c9a24af2e207e5cb7bb5e1452157e95695770225 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 03:46:03 -0400 Subject: [PATCH 085/386] Roll src/third_party/skia cf3594b60362..538e358b0d82 (4 commits) (#17251) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index cf059af47ced0..a80c47a0753eb 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'cf3594b60362996ce18dc40bd39c7de77e4ceaa7', + 'skia_revision': '538e358b0d82bcf155943cfe34ef5f924b39ef7e', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index d110c0a9b65aa..5b3c3075c61b8 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 705befc08806b2ed6990af599f1dddd7 +Signature: a4d09cdc4a4c896c25db5bdb286e2805 UNUSED LICENSES: From ba26ce3a02855ec9521d1ed9f711a8bb9e688825 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 06:16:02 -0400 Subject: [PATCH 086/386] Roll src/third_party/dart 861708483217..bdf14543576a (5 commits) (#17252) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index a80c47a0753eb..94a113154397a 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '861708483217c9c1ae2d467d647c67cd419bd064', + 'dart_revision': 'bdf14543576ad2e40e31b48a382d87ab29c66286', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From eec7447269a3dd336fcaf9840bc593495cc76097 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 15:51:02 -0400 Subject: [PATCH 087/386] Roll src/third_party/dart bdf14543576a..b7455646f90d (1 commits) (#17253) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 94a113154397a..9b9e5177569ed 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'bdf14543576ad2e40e31b48a382d87ab29c66286', + 'dart_revision': 'b7455646f90deea194a2c27c860b53345b081e9c', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From aa26106359daafa06c98bb07845f0dd915e5eff6 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 16:16:03 -0400 Subject: [PATCH 088/386] Roll fuchsia/sdk/core/mac-amd64 from nmCXC... to bgp6U... (#17254) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 9b9e5177569ed..1aed102a0b836 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'nmCXCpUyObjvw-oyDUvCc07L3WmQYlOWC-XSBAfGMF0C' + 'version': 'bgp6UDPlXYr-fzDiAAaeAqAlLZ-H2heFg0kPZUOltqAC' } ], 'condition': 'host_os == "mac"', From 209e78650ba7af7a5d2372ac0add05ac9aa3e994 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 16:31:02 -0400 Subject: [PATCH 089/386] Roll fuchsia/sdk/core/linux-amd64 from kGu6P... to k4zbT... (#17255) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1aed102a0b836..31a484d5635d5 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'kGu6P9EmEzY73hvUI6dqHTOmc_HFLa2x5EyBRrerwc4C' + 'version': 'k4zbTWRNO53PpQm139ZTqzvjPDtpXSv79FwB4AVDENMC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index f1ea2e0074b09..929e8dcecb537 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 2328197eb79c8fd85e24500b21e9ed30 +Signature: a6540eaa1ff301b1cfb74442ab338556 UNUSED LICENSES: From 9c64029758a9c99dabe5b12792705f86442f263d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 22 Mar 2020 14:03:22 -0700 Subject: [PATCH 090/386] Documentation cleanups to RuntimeController. (#17256) Based on feedback gather in https://github.com/flutter/engine/pull/17250 after it landed. --- runtime/runtime_controller.h | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index 4d91dfabf1bd4..33b51fe0690b7 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -33,10 +33,10 @@ class Window; /// normal operation, a single instance of this object is owned by the engine /// per shell. This object may only be created, used, and collected on the UI /// task runner. Window state queried by the root isolate is stored by this -/// object. In cold-restart scenarios, the engine may collect this collect -/// before installing a new runtime controller in its place. The Clone method -/// may be used by the engine to copy the currently accumulated window state so -/// it can be referenced by the new runtime controller. +/// object. In cold-restart scenarios, the engine may collect this before +/// installing a new runtime controller in its place. The Clone method may be +/// used by the engine to copy the currently accumulated window state so it can +/// be referenced by the new runtime controller. /// class RuntimeController final : public WindowClient { public: @@ -225,7 +225,7 @@ class RuntimeController final : public WindowClient { /// /// @param[in] frame_time The point at which the current frame interval /// began. May be used by animation interpolators, - /// physics simulations, etc.. + /// physics simulations, etc. /// /// @return If notification to begin frame rendering was delivered to the /// running isolate. @@ -267,12 +267,9 @@ class RuntimeController final : public WindowClient { /// manner that interferes as little as possible with frame /// rendering. /// - /// NotifyIdle is advisory and not making the same or making it with - /// insufficient deadlines does not mean that the VM will keep consuming - /// memory indefinitely. Even if the engine made absolutely no calls to - /// NotifyIdle, the VM would still perform garbage collection. The only issue - /// in such a world would be that the GC pause would happen in the middle of a - /// frame workload. + /// NotifyIdle is advisory. The VM may or may not run a garbage collection + /// when this is called, and will eventually perform garbage collections even + /// if it is not called or it is called with insufficient deadlines. /// /// The garbage collection mechanism and its thresholds are internal /// implementation details and absolutely no guarantees are made about the @@ -409,7 +406,7 @@ class RuntimeController final : public WindowClient { /// @brief Returns if the root isolate has any live receive ports. /// /// @return True if there are live receive ports, False otherwise. Return - /// False is the root isolate is not running as well. + /// False if the root isolate is not running as well. /// bool HasLivePorts(); From 80846b0d67f8d3f515e281c7f1157733ceeddbef Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 22 Mar 2020 20:26:02 -0400 Subject: [PATCH 091/386] Roll src/third_party/dart b7455646f90d..cbd67124d10e (3 commits) (#17257) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 31a484d5635d5..3375281a99657 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'b7455646f90deea194a2c27c860b53345b081e9c', + 'dart_revision': 'cbd67124d10e277e52e94f37d32aa517afeb549a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From c0deb23612f3a214b05d39ddd2b3e1d1e0fad8e1 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 22 Mar 2020 19:16:21 -0700 Subject: [PATCH 092/386] =?UTF-8?q?Don=E2=80=99t=20depend=20on=20an=20impl?= =?UTF-8?q?icit=20transaction=20when=20no=20external=20view=20embedder=20i?= =?UTF-8?q?s=20present.=20(#17258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All Metal layers have their presentsWithTransaction property set to true. However, when an external view embedder is not present, there is no mechanism to ensure that the command buffer commit is within transaction scope. This works in most cases as there there is usually an implicit (possibly nested) transaction in place during rendering. However, when there isn’t, rendering will look paused at an incorrect size. This code now works similar to OpenGL but will be refactored for ease of understanding and consistency between the various backends. --- shell/gpu/gpu_surface_metal.mm | 40 +++++++++------------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/shell/gpu/gpu_surface_metal.mm b/shell/gpu/gpu_surface_metal.mm index 154e951319bdb..5ca8b6b4a42d5 100644 --- a/shell/gpu/gpu_surface_metal.mm +++ b/shell/gpu/gpu_surface_metal.mm @@ -39,32 +39,25 @@ } // |Surface| -std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& size) { +std::unique_ptr GPUSurfaceMetal::AcquireFrame(const SkISize& frame_size) { if (!IsValid()) { FML_LOG(ERROR) << "Metal surface was invalid."; return nullptr; } - if (size.isEmpty()) { + if (frame_size.isEmpty()) { FML_LOG(ERROR) << "Metal surface was asked for an empty frame."; return nullptr; } - const auto bounds = layer_.get().bounds.size; - const auto scale = layer_.get().contentsScale; - if (bounds.width <= 0.0 || bounds.height <= 0.0 || scale <= 0.0) { - FML_LOG(ERROR) << "Metal layer bounds were invalid."; - return nullptr; - } + const auto drawable_size = CGSizeMake(frame_size.width(), frame_size.height()); - const auto scaled_bounds = CGSizeMake(bounds.width * scale, bounds.height * scale); + if (!CGSizeEqualToSize(drawable_size, layer_.get().drawableSize)) { + layer_.get().drawableSize = drawable_size; + } ReleaseUnusedDrawableIfNecessary(); - if (!CGSizeEqualToSize(scaled_bounds, layer_.get().drawableSize)) { - layer_.get().drawableSize = scaled_bounds; - } - auto surface = SkSurface::MakeFromCAMetalLayer(context_.get(), // context layer_.get(), // layer kTopLeft_GrSurfaceOrigin, // origin @@ -89,8 +82,6 @@ return false; } - const auto has_external_view_embedder = delegate_->GetExternalViewEmbedder() != nullptr; - auto command_buffer = fml::scoped_nsprotocol>([[command_queue_.get() commandBuffer] retain]); @@ -98,21 +89,10 @@ reinterpret_cast>(next_drawable_)); next_drawable_ = nullptr; - // External views need to present with transaction. When presenting with - // transaction, we have to block, otherwise we risk presenting the drawable - // after the CATransaction has completed. - // See: - // https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction - // TODO(dnfield): only do this if transactions are actually being used. - // https://github.com/flutter/flutter/issues/24133 - if (!has_external_view_embedder) { - [command_buffer.get() presentDrawable:drawable.get()]; - [command_buffer.get() commit]; - } else { - [command_buffer.get() commit]; - [command_buffer.get() waitUntilScheduled]; - [drawable.get() present]; - } + [command_buffer.get() commit]; + [command_buffer.get() waitUntilScheduled]; + [drawable.get() present]; + return true; }; From e56b335ade83298d73271df9f3c3dbaf12cbde61 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Sun, 22 Mar 2020 22:36:03 -0700 Subject: [PATCH 093/386] Allow external texture sources when using the Metal backend. (#17154) --- ci/licenses_golden/licenses_flutter | 3 + flow/texture.cc | 11 +- fml/BUILD.gn | 8 +- fml/platform/darwin/cf_utils.h | 25 ++- fml/platform/darwin/cf_utils_unittests.mm | 63 ++++++++ shell/platform/darwin/ios/BUILD.gn | 2 + shell/platform/darwin/ios/ios_context.h | 16 ++ shell/platform/darwin/ios/ios_context_gl.h | 5 + shell/platform/darwin/ios/ios_context_gl.mm | 8 + shell/platform/darwin/ios/ios_context_metal.h | 7 + .../platform/darwin/ios/ios_context_metal.mm | 27 +++- .../darwin/ios/ios_context_software.h | 5 + .../darwin/ios/ios_context_software.mm | 14 ++ .../darwin/ios/ios_external_texture_gl.mm | 2 +- .../darwin/ios/ios_external_texture_metal.h | 58 +++++++ .../darwin/ios/ios_external_texture_metal.mm | 143 ++++++++++++++++++ .../platform/darwin/ios/platform_view_ios.mm | 7 +- 17 files changed, 390 insertions(+), 14 deletions(-) create mode 100644 fml/platform/darwin/cf_utils_unittests.mm create mode 100644 shell/platform/darwin/ios/ios_external_texture_metal.h create mode 100644 shell/platform/darwin/ios/ios_external_texture_metal.mm diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7a06e1efa04bd..895c098e2d10a 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -189,6 +189,7 @@ FILE: ../../../flutter/fml/platform/android/scoped_java_ref.cc FILE: ../../../flutter/fml/platform/android/scoped_java_ref.h FILE: ../../../flutter/fml/platform/darwin/cf_utils.cc FILE: ../../../flutter/fml/platform/darwin/cf_utils.h +FILE: ../../../flutter/fml/platform/darwin/cf_utils_unittests.mm FILE: ../../../flutter/fml/platform/darwin/message_loop_darwin.h FILE: ../../../flutter/fml/platform/darwin/message_loop_darwin.mm FILE: ../../../flutter/fml/platform/darwin/paths_darwin.mm @@ -890,6 +891,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_context_software.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_context_software.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_gl.mm +FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_metal.h +FILE: ../../../flutter/shell/platform/darwin/ios/ios_external_texture_metal.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_render_target_gl.h FILE: ../../../flutter/shell/platform/darwin/ios/ios_render_target_gl.mm FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface.h diff --git a/flow/texture.cc b/flow/texture.cc index 15c93d360366e..c81314bf017fe 100644 --- a/flow/texture.cc +++ b/flow/texture.cc @@ -13,12 +13,19 @@ Texture::~Texture() = default; TextureRegistry::TextureRegistry() = default; void TextureRegistry::RegisterTexture(std::shared_ptr texture) { + if (!texture) { + return; + } mapping_[texture->Id()] = texture; } void TextureRegistry::UnregisterTexture(int64_t id) { - mapping_[id]->OnTextureUnregistered(); - mapping_.erase(id); + auto found = mapping_.find(id); + if (found == mapping_.end()) { + return; + } + found->second->OnTextureUnregistered(); + mapping_.erase(found); } void TextureRegistry::OnGrContextCreated() { diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 3a0b77515c4d8..08d60aaad7b9f 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -252,7 +252,6 @@ executable("fml_unittests") { "message_loop_unittests.cc", "message_unittests.cc", "paths_unittests.cc", - "platform/darwin/string_range_sanitization_unittests.mm", "synchronization/count_down_latch_unittests.cc", "synchronization/semaphore_unittest.cc", "synchronization/sync_switch_unittest.cc", @@ -264,6 +263,13 @@ executable("fml_unittests") { "time/time_unittest.cc", ] + if (is_mac) { + sources += [ + "platform/darwin/cf_utils_unittests.mm", + "platform/darwin/string_range_sanitization_unittests.mm", + ] + } + deps = [ ":fml_fixtures", "//flutter/fml", diff --git a/fml/platform/darwin/cf_utils.h b/fml/platform/darwin/cf_utils.h index f6be04abaaa71..00ed82d1f4411 100644 --- a/fml/platform/darwin/cf_utils.h +++ b/fml/platform/darwin/cf_utils.h @@ -18,6 +18,21 @@ class CFRef { CFRef(T instance) : instance_(instance) {} + CFRef(const CFRef& other) : instance_(other.instance_) { + if (instance_) { + CFRetain(instance_); + } + } + + CFRef(CFRef&& other) : instance_(other.instance_) { + other.instance_ = nullptr; + } + + CFRef& operator=(CFRef&& other) { + Reset(other.Release()); + return *this; + } + ~CFRef() { if (instance_ != nullptr) { CFRelease(instance_); @@ -25,7 +40,7 @@ class CFRef { instance_ = nullptr; } - void Reset(T instance) { + void Reset(T instance = nullptr) { if (instance_ == instance) { return; } @@ -36,6 +51,12 @@ class CFRef { instance_ = instance; } + [[nodiscard]] T Release() { + auto instance = instance_; + instance_ = nullptr; + return instance; + } + operator T() const { return instance_; } operator bool() const { return instance_ != nullptr; } @@ -43,7 +64,7 @@ class CFRef { private: T instance_; - FML_DISALLOW_COPY_AND_ASSIGN(CFRef); + CFRef& operator=(const CFRef&) = delete; }; } // namespace fml diff --git a/fml/platform/darwin/cf_utils_unittests.mm b/fml/platform/darwin/cf_utils_unittests.mm new file mode 100644 index 0000000000000..9df1f5f6ddfd3 --- /dev/null +++ b/fml/platform/darwin/cf_utils_unittests.mm @@ -0,0 +1,63 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/platform/darwin/cf_utils.h" +#include "flutter/testing/testing.h" + +namespace fml { +namespace testing { + +TEST(CFTest, CanCreateRefs) { + CFRef string(CFStringCreateMutable(kCFAllocatorDefault, 100u)); + // Cast + ASSERT_TRUE(static_cast(string)); + ASSERT_TRUE(string); + + const auto ref_count = CFGetRetainCount(string); + + // Copy & Reset + { + CFRef string2 = string; + ASSERT_TRUE(string2); + ASSERT_EQ(ref_count + 1u, CFGetRetainCount(string)); + ASSERT_EQ(CFGetRetainCount(string2), CFGetRetainCount(string)); + + string2.Reset(); + ASSERT_FALSE(string2); + ASSERT_EQ(ref_count, CFGetRetainCount(string)); + } + + // Release + { + auto string3 = string; + ASSERT_TRUE(string3); + ASSERT_EQ(ref_count + 1u, CFGetRetainCount(string)); + auto raw_string3 = string3.Release(); + ASSERT_FALSE(string3); + ASSERT_EQ(ref_count + 1u, CFGetRetainCount(string)); + CFRelease(raw_string3); + ASSERT_EQ(ref_count, CFGetRetainCount(string)); + } + + // Move + { + auto string_source = string; + ASSERT_TRUE(string_source); + auto string_move = std::move(string_source); + ASSERT_FALSE(string_source); + ASSERT_EQ(ref_count + 1u, CFGetRetainCount(string)); + string_move.Reset(); + ASSERT_EQ(ref_count, CFGetRetainCount(string)); + } + + // Move assign. + { + auto string_move_assign = std::move(string); + ASSERT_FALSE(string); + ASSERT_EQ(ref_count, CFGetRetainCount(string_move_assign)); + } +} + +} // namespace testing +} // namespace fml diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 1671975c8d0e5..7af51b5aa230d 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -131,6 +131,8 @@ shared_library("create_flutter_framework_dylib") { sources += [ "ios_context_metal.h", "ios_context_metal.mm", + "ios_external_texture_metal.h", + "ios_external_texture_metal.mm", "ios_surface_metal.h", "ios_surface_metal.mm", ] diff --git a/shell/platform/darwin/ios/ios_context.h b/shell/platform/darwin/ios/ios_context.h index 6615821905807..37fccd4b3de2e 100644 --- a/shell/platform/darwin/ios/ios_context.h +++ b/shell/platform/darwin/ios/ios_context.h @@ -7,8 +7,10 @@ #include +#include "flutter/flow/texture.h" #include "flutter/fml/macros.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h" #include "flutter/shell/platform/darwin/ios/rendering_api_selection.h" #include "third_party/skia/include/gpu/GrContext.h" @@ -119,6 +121,20 @@ class IOSContext { /// virtual bool ClearCurrent() = 0; + //---------------------------------------------------------------------------- + /// @brief Creates an external texture proxy of the appropriate client + /// rendering API. + /// + /// @param[in] texture_id The texture identifier + /// @param[in] texture The texture + /// + /// @return The texture proxy if the rendering backend supports embedder + /// provided external textures. + /// + virtual std::unique_ptr CreateExternalTexture( + int64_t texture_id, + fml::scoped_nsobject> texture) = 0; + protected: IOSContext(); diff --git a/shell/platform/darwin/ios/ios_context_gl.h b/shell/platform/darwin/ios/ios_context_gl.h index fd8885a42395a..56308831b954c 100644 --- a/shell/platform/darwin/ios/ios_context_gl.h +++ b/shell/platform/darwin/ios/ios_context_gl.h @@ -40,6 +40,11 @@ class IOSContextGL final : public IOSContext { // |IOSContext| bool ResourceMakeCurrent() override; + // |IOSContext| + std::unique_ptr CreateExternalTexture( + int64_t texture_id, + fml::scoped_nsobject> texture) override; + FML_DISALLOW_COPY_AND_ASSIGN(IOSContextGL); }; diff --git a/shell/platform/darwin/ios/ios_context_gl.mm b/shell/platform/darwin/ios/ios_context_gl.mm index 1396198338193..2a5a32bf8c5cb 100644 --- a/shell/platform/darwin/ios/ios_context_gl.mm +++ b/shell/platform/darwin/ios/ios_context_gl.mm @@ -8,6 +8,7 @@ #include "flutter/shell/common/shell_io_manager.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" +#include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" namespace flutter { @@ -58,4 +59,11 @@ return [EAGLContext setCurrentContext:nil]; } +// |IOSContext| +std::unique_ptr IOSContextGL::CreateExternalTexture( + int64_t texture_id, + fml::scoped_nsobject> texture) { + return std::make_unique(texture_id, std::move(texture)); +} + } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_context_metal.h b/shell/platform/darwin/ios/ios_context_metal.h index f98b11f72d9cc..194b3fc4a0084 100644 --- a/shell/platform/darwin/ios/ios_context_metal.h +++ b/shell/platform/darwin/ios/ios_context_metal.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "flutter/fml/platform/darwin/cf_utils.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/platform/darwin/ios/ios_context.h" #include "third_party/skia/include/gpu/GrContext.h" @@ -35,6 +36,7 @@ class IOSContextMetal final : public IOSContext { fml::scoped_nsprotocol> main_queue_; sk_sp main_context_; sk_sp resource_context_; + fml::CFRef texture_cache_; bool is_valid_ = false; // |IOSContext| @@ -49,6 +51,11 @@ class IOSContextMetal final : public IOSContext { // |IOSContext| bool ClearCurrent() override; + // |IOSContext| + std::unique_ptr CreateExternalTexture( + int64_t texture_id, + fml::scoped_nsobject> texture) override; + FML_DISALLOW_COPY_AND_ASSIGN(IOSContextMetal); }; diff --git a/shell/platform/darwin/ios/ios_context_metal.mm b/shell/platform/darwin/ios/ios_context_metal.mm index 8b9cbda33455b..eb16aab4a7008 100644 --- a/shell/platform/darwin/ios/ios_context_metal.mm +++ b/shell/platform/darwin/ios/ios_context_metal.mm @@ -5,20 +5,21 @@ #include "flutter/shell/platform/darwin/ios/ios_context_metal.h" #include "flutter/fml/logging.h" +#include "flutter/shell/platform/darwin/ios/ios_external_texture_metal.h" namespace flutter { IOSContextMetal::IOSContextMetal() { device_.reset([MTLCreateSystemDefaultDevice() retain]); if (!device_) { - FML_LOG(ERROR) << "Could not acquire Metal device."; + FML_DLOG(ERROR) << "Could not acquire Metal device."; return; } main_queue_.reset([device_ newCommandQueue]); if (!main_queue_) { - FML_LOG(ERROR) << "Could not create Metal command queue."; + FML_DLOG(ERROR) << "Could not create Metal command queue."; return; } @@ -30,10 +31,23 @@ resource_context_ = GrContext::MakeMetal([device_ retain], [main_queue_ retain]); if (!main_context_ || !resource_context_) { - FML_LOG(ERROR) << "Could not create Skia Metal contexts."; + FML_DLOG(ERROR) << "Could not create Skia Metal contexts."; return; } + CVMetalTextureCacheRef texture_cache_raw = NULL; + auto cv_return = CVMetalTextureCacheCreate(kCFAllocatorDefault, // allocator + NULL, // cache attributes (NULL default) + device_.get(), // metal device + NULL, // texture attributes (NULL default) + &texture_cache_raw // [out] cache + ); + if (cv_return != kCVReturnSuccess) { + FML_DLOG(ERROR) << "Could not create Metal texture cache."; + return; + } + texture_cache_.Reset(texture_cache_raw); + is_valid_ = false; } @@ -83,4 +97,11 @@ return true; } +// |IOSContext| +std::unique_ptr IOSContextMetal::CreateExternalTexture( + int64_t texture_id, + fml::scoped_nsobject> texture) { + return std::make_unique(texture_id, texture_cache_, std::move(texture)); +} + } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_context_software.h b/shell/platform/darwin/ios/ios_context_software.h index 558f39a637f97..0bd11c4538b43 100644 --- a/shell/platform/darwin/ios/ios_context_software.h +++ b/shell/platform/darwin/ios/ios_context_software.h @@ -29,6 +29,11 @@ class IOSContextSoftware final : public IOSContext { // |IOSContext| bool ClearCurrent() override; + // |IOSContext| + std::unique_ptr CreateExternalTexture( + int64_t texture_id, + fml::scoped_nsobject> texture) override; + private: FML_DISALLOW_COPY_AND_ASSIGN(IOSContextSoftware); }; diff --git a/shell/platform/darwin/ios/ios_context_software.mm b/shell/platform/darwin/ios/ios_context_software.mm index 8bb029d5792d3..ed11bd542404d 100644 --- a/shell/platform/darwin/ios/ios_context_software.mm +++ b/shell/platform/darwin/ios/ios_context_software.mm @@ -31,4 +31,18 @@ return false; } +// |IOSContext| +std::unique_ptr IOSContextSoftware::CreateExternalTexture( + int64_t texture_id, + fml::scoped_nsobject> texture) { + // Don't use FML for logging as it will contain engine specific details. This is a user facing + // message. + NSLog(@"Flutter: Attempted to composite external texture sources using the software backend. " + @"This backend is only used on simulators. This feature is only available on actual " + @"devices where OpenGL or Metal is used for rendering."); + + // Not supported in this backend. + return nullptr; +} + } // namespace flutter diff --git a/shell/platform/darwin/ios/ios_external_texture_gl.mm b/shell/platform/darwin/ios/ios_external_texture_gl.mm index 19cb324ca8c62..697992d1b7cd8 100644 --- a/shell/platform/darwin/ios/ios_external_texture_gl.mm +++ b/shell/platform/darwin/ios/ios_external_texture_gl.mm @@ -56,7 +56,7 @@ bool IOSExternalTextureGL::NeedUpdateTexture(bool freeze) { // Update texture if `texture_ref_` is reset to `nullptr` when GrContext - // is destroied or new frame is ready. + // is destroyed or new frame is ready. return (!freeze && new_frame_ready_) || !texture_ref_; } diff --git a/shell/platform/darwin/ios/ios_external_texture_metal.h b/shell/platform/darwin/ios/ios_external_texture_metal.h new file mode 100644 index 0000000000000..82e4d6931d426 --- /dev/null +++ b/shell/platform/darwin/ios/ios_external_texture_metal.h @@ -0,0 +1,58 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_EXTERNAL_TEXTURE_METAL_H_ +#define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_EXTERNAL_TEXTURE_METAL_H_ + +#include + +#import + +#include "flutter/flow/texture.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/platform/darwin/cf_utils.h" +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterTexture.h" +#include "third_party/skia/include/core/SkImage.h" + +namespace flutter { + +class IOSExternalTextureMetal final : public Texture { + public: + IOSExternalTextureMetal(int64_t texture_id, + fml::CFRef texture_cache, + fml::scoped_nsobject> external_texture); + + // |Texture| + ~IOSExternalTextureMetal(); + + private: + fml::CFRef texture_cache_; + fml::scoped_nsobject> external_texture_; + std::atomic_bool texture_frame_available_; + sk_sp external_image_; + + // |Texture| + void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrContext* context) override; + + // |Texture| + void OnGrContextCreated() override; + + // |Texture| + void OnGrContextDestroyed() override; + + // |Texture| + void MarkNewFrameAvailable() override; + + // |Texture| + void OnTextureUnregistered() override; + + sk_sp WrapExternalPixelBuffer(GrContext* context); + + FML_DISALLOW_COPY_AND_ASSIGN(IOSExternalTextureMetal); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_IOS_EXTERNAL_TEXTURE_METAL_H_ diff --git a/shell/platform/darwin/ios/ios_external_texture_metal.mm b/shell/platform/darwin/ios/ios_external_texture_metal.mm new file mode 100644 index 0000000000000..7ab55c94b131d --- /dev/null +++ b/shell/platform/darwin/ios/ios_external_texture_metal.mm @@ -0,0 +1,143 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/darwin/ios/ios_external_texture_metal.h" + +#include "flutter/fml/logging.h" +#include "third_party/skia/include/gpu/GrBackendSurface.h" +#include "third_party/skia/include/gpu/mtl/GrMtlTypes.h" + +namespace flutter { + +IOSExternalTextureMetal::IOSExternalTextureMetal( + int64_t texture_id, + fml::CFRef texture_cache, + fml::scoped_nsobject> external_texture) + : Texture(texture_id), + texture_cache_(std::move(texture_cache)), + external_texture_(std::move(external_texture)) { + FML_DCHECK(texture_cache_); + FML_DCHECK(external_texture_); +} + +IOSExternalTextureMetal::~IOSExternalTextureMetal() = default; + +void IOSExternalTextureMetal::Paint(SkCanvas& canvas, + const SkRect& bounds, + bool freeze, + GrContext* context) { + if (!freeze && texture_frame_available_) { + external_image_ = nullptr; + } + + if (!external_image_) { + external_image_ = WrapExternalPixelBuffer(context); + texture_frame_available_ = false; + } + + if (external_image_) { + canvas.drawImageRect(external_image_, // image + external_image_->bounds(), // source rect + bounds, // destination rect + nullptr, // paint + SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint // constraint + ); + } +} + +sk_sp IOSExternalTextureMetal::WrapExternalPixelBuffer(GrContext* context) { + auto pixel_buffer = fml::CFRef([external_texture_ copyPixelBuffer]); + if (!pixel_buffer) { + return nullptr; + } + + auto texture_size = + SkISize::Make(CVPixelBufferGetWidth(pixel_buffer), CVPixelBufferGetHeight(pixel_buffer)); + + CVMetalTextureRef metal_texture_raw = NULL; + auto cv_return = + CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, // allocator + texture_cache_, // texture cache + pixel_buffer, // source image + NULL, // texture attributes + MTLPixelFormatBGRA8Unorm, // pixel format + texture_size.width(), // width + texture_size.height(), // height + 0u, // plane index + &metal_texture_raw // [out] texture + ); + + if (cv_return != kCVReturnSuccess) { + FML_DLOG(ERROR) << "Could not create Metal texture from pixel buffer: CVReturn " << cv_return; + return nullptr; + } + + fml::CFRef metal_texture(metal_texture_raw); + + GrMtlTextureInfo skia_texture_info; + skia_texture_info.fTexture = sk_cf_obj{ + [reinterpret_cast(CVMetalTextureGetTexture(metal_texture)) retain]}; + + GrBackendTexture skia_backend_texture(texture_size.width(), // width + texture_size.height(), // height + GrMipMapped ::kNo, // mip-mapped + skia_texture_info // texture info + ); + + struct ImageCaptures { + fml::CFRef buffer; + fml::CFRef texture; + }; + + auto captures = std::make_unique(); + captures->buffer = std::move(pixel_buffer); + captures->texture = std::move(metal_texture); + + SkImage::TextureReleaseProc release_proc = [](SkImage::ReleaseContext release_context) { + auto captures = reinterpret_cast(release_context); + delete captures; + }; + + auto image = SkImage::MakeFromTexture(context, // context + skia_backend_texture, // backend texture + kTopLeft_GrSurfaceOrigin, // origin + kBGRA_8888_SkColorType, // color type + kPremul_SkAlphaType, // alpha type + nullptr, // color space + release_proc, // release proc + captures.release() // release context + + ); + + if (!image) { + FML_DLOG(ERROR) << "Could not wrap Metal texture as a Skia image."; + } + + return image; +} + +void IOSExternalTextureMetal::OnGrContextCreated() { + // External images in this backend have no thread affinity and are not tied to the context in any + // way. Instead, they are tied to the Metal device which is associated with the cache already and + // is consistent throughout the shell run. +} + +void IOSExternalTextureMetal::OnGrContextDestroyed() { + external_image_.reset(); + CVMetalTextureCacheFlush(texture_cache_, // cache + 0 // options (must be zero) + ); +} + +void IOSExternalTextureMetal::MarkNewFrameAvailable() { + texture_frame_available_ = true; +} + +void IOSExternalTextureMetal::OnTextureUnregistered() { + if ([external_texture_ respondsToSelector:@selector(onTextureUnregistered:)]) { + [external_texture_ onTextureUnregistered:external_texture_]; + } +} + +} // namespace flutter diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 09d19b4e61699..53b70968b341d 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -4,18 +4,14 @@ #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" -#import - #include #include "flutter/common/task_runners.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/trace_event.h" #include "flutter/shell/common/shell_io_manager.h" -#include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" -#include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h" namespace flutter { @@ -92,7 +88,8 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), void PlatformViewIOS::RegisterExternalTexture(int64_t texture_id, NSObject* texture) { - RegisterTexture(std::make_shared(texture_id, texture)); + RegisterTexture(ios_context_->CreateExternalTexture( + texture_id, fml::scoped_nsobject>{[texture retain]})); } // |PlatformView| From 35e83036332276c2d46452c959a59ca95be5d1ab Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 02:11:03 -0400 Subject: [PATCH 094/386] Roll src/third_party/skia 538e358b0d82..f04f21a5c913 (2 commits) (#17260) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 3375281a99657..c1978fada63e9 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '538e358b0d82bcf155943cfe34ef5f924b39ef7e', + 'skia_revision': 'f04f21a5c91357352a41b55a357666702064a5f5', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 5b3c3075c61b8..53e96be8b887b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: a4d09cdc4a4c896c25db5bdb286e2805 +Signature: 8f53050411db4cb0f5aea62f7ebf48b7 UNUSED LICENSES: From c86a4ccd00dc481efea2730c486eb1172d9f0565 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 07:01:03 -0400 Subject: [PATCH 095/386] Roll fuchsia/sdk/core/mac-amd64 from bgp6U... to WaNBa... (#17263) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index c1978fada63e9..24e9dc637d92a 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'bgp6UDPlXYr-fzDiAAaeAqAlLZ-H2heFg0kPZUOltqAC' + 'version': 'WaNBaYEWWIMn_T7f0ejsBbfLkEsYWF5hk_JgUjUKkO4C' } ], 'condition': 'host_os == "mac"', From da9e7788a7f414693f558532345d5d578a6b17c7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 07:16:03 -0400 Subject: [PATCH 096/386] Roll fuchsia/sdk/core/linux-amd64 from k4zbT... to h8m0G... (#17264) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 24e9dc637d92a..469f98f24d9b6 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'k4zbTWRNO53PpQm139ZTqzvjPDtpXSv79FwB4AVDENMC' + 'version': 'h8m0GOzmMI7qIReFkpBbj1kFWGCbEyf02lBE9zSxTKsC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 929e8dcecb537..4a151c2a89c7d 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: a6540eaa1ff301b1cfb74442ab338556 +Signature: fc6e387341f6b181ccee7bd540135291 UNUSED LICENSES: From 11f55218778b7ec0cfb0117bb7805c7e27b09c42 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 11:26:01 -0400 Subject: [PATCH 097/386] Roll src/third_party/skia f04f21a5c913..6f60a859c628 (2 commits) (#17268) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 469f98f24d9b6..8aaaf83eaf55f 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f04f21a5c91357352a41b55a357666702064a5f5', + 'skia_revision': '6f60a859c628ccf4e97986782cf795574a7248d6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 53e96be8b887b..ce1c8a013c960 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 8f53050411db4cb0f5aea62f7ebf48b7 +Signature: ee71919fe2c43e1dc6ea0b98b18c9841 UNUSED LICENSES: From 8a8b298405842034565faa37b6be1c403a0feacf Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 23 Mar 2020 10:44:53 -0700 Subject: [PATCH 098/386] Added a log message when sharing a FlutterEngine across multiple FlutterViewControllers. (#17186) --- .../darwin/ios/framework/Source/FlutterViewController.mm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 6deed5f8104d7..6e7e8c495828d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -144,6 +144,14 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine self = [super initWithNibName:nibName bundle:nibBundle]; if (self) { _viewOpaque = YES; + if (engine.viewController) { + FML_LOG(ERROR) << "The supplied FlutterEngine " << [[engine description] UTF8String] + << " is already used with FlutterViewController instance " + << [[engine.viewController description] UTF8String] + << ". One instance of the FlutterEngine can only be attached to one " + "FlutterViewController at a time. Set FlutterEngine.viewController " + "to nil before attaching it to another FlutterViewController."; + } _engine.reset([engine retain]); _engineNeedsLaunch = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); From 33a21d1ff8ad3290e13a9570f68250452470ee6a Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 23 Mar 2020 10:46:10 -0700 Subject: [PATCH 099/386] Add DartProject for Windows embedding API (#17210) This is a step toward aligning the API with macOS, and will make it easier to add the precompiled library later for release mode (since it can just be added to the project directory, without any code changes required for wrapper clients). At the C API, uses a struct instead of individual arguments, mirroring a change that was already made on the Linux side to make the C API cleaner. Functional changes in addition to the restructuring: adds relative path support, as was recently added for GLFW Uses wstring, rather than string, for paths; the conversion to UTF-8 is actually a potential problem on Windows, so pushing it into the embedding allows us the possibility of removing it later (if we can figure out a good solution at the embedder.h layer) without API breakage. The old APIs used by the standard runner are left in place for now to avoid breaking the template on an engine roll. Once the framework template has been updated, the old API paths will be removed. --- ci/licenses_golden/licenses_flutter | 2 + .../framework/Headers/FlutterDartProject.h | 2 +- .../platform/windows/client_wrapper/BUILD.gn | 2 + .../client_wrapper/dart_project_unittests.cc | 46 +++++++++++ .../client_wrapper/flutter_view_controller.cc | 30 ++++++- .../flutter_view_controller_unittests.cc | 24 +++--- .../include/flutter/dart_project.h | 65 ++++++++++++++++ .../include/flutter/flutter_view_controller.h | 22 ++---- .../testing/stub_flutter_windows_api.cc | 26 +++++-- .../testing/stub_flutter_windows_api.h | 17 ++-- shell/platform/windows/flutter_windows.cc | 78 +++++++++++++------ .../platform/windows/public/flutter_windows.h | 67 +++++++++------- 12 files changed, 281 insertions(+), 100 deletions(-) create mode 100644 shell/platform/windows/client_wrapper/dart_project_unittests.cc create mode 100644 shell/platform/windows/client_wrapper/include/flutter/dart_project.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 895c098e2d10a..c2a8ee725502c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1145,9 +1145,11 @@ FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.cc FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.h +FILE: ../../../flutter/shell/platform/windows/client_wrapper/dart_project_unittests.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_view_controller.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_view_controller_unittests.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_view_unittests.cc +FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plugin_registrar_windows.h diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h b/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h index 0e3788a33d0dc..fe35a52722fe8 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterDartProject.h @@ -36,7 +36,7 @@ FLUTTER_EXPORT * guarantee, and are subject to change without notice. * * Note: This property WILL BE REMOVED in the future. If you use this property, please see - * https://github.com/flutter/flutter/issue/38569. + * https://github.com/flutter/flutter/issues/38569. */ @property(nullable) NSArray* engineSwitches; diff --git a/shell/platform/windows/client_wrapper/BUILD.gn b/shell/platform/windows/client_wrapper/BUILD.gn index c697578f0d60b..f92640e80f7f7 100644 --- a/shell/platform/windows/client_wrapper/BUILD.gn +++ b/shell/platform/windows/client_wrapper/BUILD.gn @@ -6,6 +6,7 @@ import("//flutter/shell/platform/common/cpp/client_wrapper/publish.gni") import("//flutter/testing/testing.gni") _wrapper_includes = [ + "include/flutter/dart_project.h", "include/flutter/flutter_view_controller.h", "include/flutter/flutter_view.h", "include/flutter/plugin_registrar_windows.h", @@ -69,6 +70,7 @@ executable("client_wrapper_windows_unittests") { testonly = true sources = [ + "dart_project_unittests.cc", "flutter_view_controller_unittests.cc", "flutter_view_unittests.cc", "plugin_registrar_windows_unittests.cc", diff --git a/shell/platform/windows/client_wrapper/dart_project_unittests.cc b/shell/platform/windows/client_wrapper/dart_project_unittests.cc new file mode 100644 index 0000000000000..e6d6c17430e0c --- /dev/null +++ b/shell/platform/windows/client_wrapper/dart_project_unittests.cc @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h" +#include "gtest/gtest.h" + +namespace flutter { + +class DartProjectTest : public ::testing::Test { + protected: + // Wrapper for accessing private icu_data_path. + std::wstring GetProjectIcuDataPath(const DartProject& project) { + return project.icu_data_path(); + } + + // Wrapper for accessing private assets_path. + std::wstring GetProjectAssetsPath(const DartProject& project) { + return project.assets_path(); + } + + // Wrapper for accessing private engine_switches. + std::vector GetProjectEngineSwitches( + const DartProject& project) { + return project.engine_switches(); + } +}; + +TEST_F(DartProjectTest, StandardProjectFormat) { + DartProject project(L"test"); + EXPECT_EQ(GetProjectIcuDataPath(project), L"test\\icudtl.dat"); + EXPECT_EQ(GetProjectAssetsPath(project), L"test\\flutter_assets"); +} + +TEST_F(DartProjectTest, Switches) { + DartProject project(L"test"); + std::vector switches = {"--foo", "--bar"}; + project.SetEngineSwitches(switches); + EXPECT_EQ(GetProjectEngineSwitches(project).size(), 2); + EXPECT_EQ(GetProjectEngineSwitches(project)[0], "--foo"); +} + +} // namespace flutter diff --git a/shell/platform/windows/client_wrapper/flutter_view_controller.cc b/shell/platform/windows/client_wrapper/flutter_view_controller.cc index 92a853862d77c..a16a0699dd188 100644 --- a/shell/platform/windows/client_wrapper/flutter_view_controller.cc +++ b/shell/platform/windows/client_wrapper/flutter_view_controller.cc @@ -9,13 +9,35 @@ namespace flutter { +FlutterViewController::FlutterViewController(int width, + int height, + const DartProject& project) { + std::vector switches; + std::transform( + project.engine_switches().begin(), project.engine_switches().end(), + std::back_inserter(switches), + [](const std::string& arg) -> const char* { return arg.c_str(); }); + size_t switch_count = switches.size(); + + FlutterDesktopEngineProperties properties = {}; + properties.assets_path = project.assets_path().c_str(); + properties.icu_data_path = project.icu_data_path().c_str(); + properties.switches = switch_count > 0 ? switches.data() : nullptr; + properties.switches_count = switch_count; + controller_ = FlutterDesktopCreateViewController(width, height, properties); + if (!controller_) { + std::cerr << "Failed to create view controller." << std::endl; + return; + } + view_ = std::make_unique(FlutterDesktopGetView(controller_)); +} + FlutterViewController::FlutterViewController( const std::string& icu_data_path, int width, int height, const std::string& assets_path, - const std::vector& arguments) - : icu_data_path_(icu_data_path) { + const std::vector& arguments) { if (controller_) { std::cerr << "Only one Flutter view can exist at a time." << std::endl; } @@ -26,8 +48,8 @@ FlutterViewController::FlutterViewController( [](const std::string& arg) -> const char* { return arg.c_str(); }); size_t arg_count = engine_arguments.size(); - controller_ = FlutterDesktopCreateViewController( - width, height, assets_path.c_str(), icu_data_path_.c_str(), + controller_ = FlutterDesktopCreateViewControllerLegacy( + width, height, assets_path.c_str(), icu_data_path.c_str(), arg_count > 0 ? &engine_arguments[0] : nullptr, arg_count); if (!controller_) { std::cerr << "Failed to create view controller." << std::endl; diff --git a/shell/platform/windows/client_wrapper/flutter_view_controller_unittests.cc b/shell/platform/windows/client_wrapper/flutter_view_controller_unittests.cc index 1ebd9683ca3d9..ec1b7fb561eae 100644 --- a/shell/platform/windows/client_wrapper/flutter_view_controller_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_view_controller_unittests.cc @@ -16,19 +16,16 @@ namespace { // Stub implementation to validate calls to the API. class TestWindowsApi : public testing::StubFlutterWindowsApi { FlutterDesktopViewControllerRef CreateViewController( - int initial_width, - int initial_height, - const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count) override { + int width, + int height, + const FlutterDesktopEngineProperties& engine_properties) override { return reinterpret_cast(1); } }; } // namespace -TEST(FlutterViewControllerTest, CreateDestroy) { +TEST(FlutterViewControllerTest, CreateDestroyLegacy) { testing::ScopedStubFlutterWindowsApi scoped_api_stub( std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); @@ -38,13 +35,20 @@ TEST(FlutterViewControllerTest, CreateDestroy) { } } +TEST(FlutterViewControllerTest, CreateDestroy) { + DartProject project(L"data"); + testing::ScopedStubFlutterWindowsApi scoped_api_stub( + std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + { FlutterViewController controller(100, 100, project); } +} + TEST(FlutterViewControllerTest, GetView) { - std::string icu_data_path = "fake_path_to_icu"; + DartProject project(L"data"); testing::ScopedStubFlutterWindowsApi scoped_api_stub( std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); - FlutterViewController controller("", 100, 100, "", - std::vector{}); + FlutterViewController controller(100, 100, project); EXPECT_NE(controller.view(), nullptr); } diff --git a/shell/platform/windows/client_wrapper/include/flutter/dart_project.h b/shell/platform/windows/client_wrapper/include/flutter/dart_project.h new file mode 100644 index 0000000000000..3595239ebd51d --- /dev/null +++ b/shell/platform/windows/client_wrapper/include/flutter/dart_project.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_DART_PROJECT_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_DART_PROJECT_H_ + +#include +#include + +namespace flutter { + +// A set of Flutter and Dart assets used to initialize a Flutter engine. +class DartProject { + public: + // Creates a DartProject from a directory path. The directory should contain + // the following top-level items: + // - icudtl.dat (provided as a resource by the Flutter tool) + // - flutter_assets (as built by the Flutter tool) + // + // The path can either be absolute, or relative to the directory containing + // the running executable. + explicit DartProject(const std::wstring& path) { + assets_path_ = path + L"\\flutter_assets"; + icu_data_path_ = path + L"\\icudtl.dat"; + } + + ~DartProject() = default; + + // Switches to pass to the Flutter engine. See + // https://github.com/flutter/engine/blob/master/shell/common/switches.h + // for details. Not all switches will apply to embedding mode. Switches have + // not stability guarantee, and are subject to change without notice. + // + // Note: This WILL BE REMOVED in the future. If you call this, please see + // https://github.com/flutter/flutter/issues/38569. + void SetEngineSwitches(const std::vector& switches) { + engine_switches_ = switches; + } + + private: + // Accessors for internals are private, so that they can be changed if more + // flexible options for project structures are needed later without it + // being a breaking change. Provide access to internal classes that need + // them. + friend class FlutterViewController; + friend class DartProjectTest; + + const std::wstring& assets_path() const { return assets_path_; } + const std::wstring& icu_data_path() const { return icu_data_path_; } + const std::vector& engine_switches() const { + return engine_switches_; + } + + // The path to the assets directory. + std::wstring assets_path_; + // The path to the ICU data. + std::wstring icu_data_path_; + // Switches to pass to the engine. + std::vector engine_switches_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_DART_PROJECT_H_ diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h index c72b285227240..607e01a72f247 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h @@ -11,6 +11,7 @@ #include #include +#include "dart_project.h" #include "flutter_view.h" #include "plugin_registrar.h" #include "plugin_registry.h" @@ -24,20 +25,15 @@ namespace flutter { // methods in the C API directly, as this class will do that internally. class FlutterViewController : public PluginRegistry { public: - // There must be only one instance of this class in an application at any - // given time, as Flutter does not support multiple engines in one process, - // or multiple views in one engine. - // Creates a FlutterView that can be parented into a Windows View hierarchy // either using HWNDs or in the future into a CoreWindow, or using compositor. - - // The |assets_path| is the path to the flutter_assets folder for the Flutter - // application to be run. |icu_data_path| is the path to the icudtl.dat file - // for the version of Flutter you are using. // - // The |arguments| are passed to the Flutter engine. See: - // https://github.com/flutter/engine/blob/master/shell/common/switches.h for - // for details. Not all arguments will apply to desktop. + // |dart_project| will be used to configure the engine backing this view. + explicit FlutterViewController(int width, + int height, + const DartProject& project); + + // DEPRECATED. Will be removed soon; use the version above. explicit FlutterViewController(const std::string& icu_data_path, int width, int height, @@ -65,10 +61,6 @@ class FlutterViewController : public PluginRegistry { const std::string& plugin_name) override; private: - // The path to the ICU data file. Set at creation time since it is the same - // for any view created. - std::string icu_data_path_; - // Handle for interacting with the C API's view controller, if any. FlutterDesktopViewControllerRef controller_ = nullptr; diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index 07dfb8bd7bbbf..b84d9195daf26 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -36,6 +36,17 @@ ScopedStubFlutterWindowsApi::~ScopedStubFlutterWindowsApi() { // Forwarding dummy implementations of the C API. FlutterDesktopViewControllerRef FlutterDesktopCreateViewController( + int width, + int height, + const FlutterDesktopEngineProperties& engine_properties) { + if (s_stub_implementation) { + return s_stub_implementation->CreateViewController(width, height, + engine_properties); + } + return nullptr; +} + +FlutterDesktopViewControllerRef FlutterDesktopCreateViewControllerLegacy( int initial_width, int initial_height, const char* assets_path, @@ -43,9 +54,11 @@ FlutterDesktopViewControllerRef FlutterDesktopCreateViewController( const char** arguments, size_t argument_count) { if (s_stub_implementation) { + // This stub will be removed shortly, and the current tests don't need the + // arguments, so there's no need to translate them to engine_properties. + FlutterDesktopEngineProperties engine_properties; return s_stub_implementation->CreateViewController( - initial_width, initial_height, assets_path, icu_data_path, arguments, - argument_count); + initial_width, initial_height, engine_properties); } return nullptr; } @@ -78,13 +91,10 @@ HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef controller) { return reinterpret_cast(-1); } -FlutterDesktopEngineRef FlutterDesktopRunEngine(const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count) { +FlutterDesktopEngineRef FlutterDesktopRunEngine( + const FlutterDesktopEngineProperties& engine_properties) { if (s_stub_implementation) { - return s_stub_implementation->RunEngine(assets_path, icu_data_path, - arguments, argument_count); + return s_stub_implementation->RunEngine(engine_properties); } return nullptr; } diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index 0fd74c5b275f5..631d445802f28 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -29,14 +29,11 @@ class StubFlutterWindowsApi { virtual ~StubFlutterWindowsApi() {} - // Called for FlutterDesktopCreateView. + // Called for FlutterDesktopCreateViewController. virtual FlutterDesktopViewControllerRef CreateViewController( - int initial_width, - int initial_height, - const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count) { + int width, + int height, + const FlutterDesktopEngineProperties& engine_properties) { return nullptr; } @@ -50,10 +47,8 @@ class StubFlutterWindowsApi { virtual HWND ViewGetHWND() { return reinterpret_cast(1); } // Called for FlutterDesktopRunEngine. - virtual FlutterDesktopEngineRef RunEngine(const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count) { + virtual FlutterDesktopEngineRef RunEngine( + const FlutterDesktopEngineProperties& engine_properties) { return nullptr; } diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 05f8dcc68aec9..25da4374b74bc 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -9,11 +9,13 @@ #include #include #include +#include #include #include #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h" #include "flutter/shell/platform/common/cpp/incoming_message_dispatcher.h" +#include "flutter/shell/platform/common/cpp/path_utils.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/dpi_utils.h" #include "flutter/shell/platform/windows/key_event_handler.h" @@ -36,18 +38,16 @@ static_assert(FLUTTER_ENGINE_VERSION == 1, ""); // engine. static std::unique_ptr RunFlutterEngine( flutter::Win32FlutterWindow* window, - const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t arguments_count) { + const FlutterDesktopEngineProperties& engine_properties) { auto state = std::make_unique(); // FlutterProjectArgs is expecting a full argv, so when processing it for // flags the first item is treated as the executable and ignored. Add a dummy // value so that all provided arguments are used. std::vector argv = {"placeholder"}; - if (arguments_count > 0) { - argv.insert(argv.end(), &arguments[0], &arguments[arguments_count]); + if (engine_properties.switches_count > 0) { + argv.insert(argv.end(), &engine_properties.switches[0], + &engine_properties.switches[engine_properties.switches_count]); } window->CreateRenderSurface(); @@ -105,10 +105,28 @@ static std::unique_ptr RunFlutterEngine( custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners); custom_task_runners.platform_task_runner = &platform_task_runner; + std::filesystem::path assets_path(engine_properties.assets_path); + std::filesystem::path icu_path(engine_properties.icu_data_path); + if (assets_path.is_relative() || icu_path.is_relative()) { + // Treat relative paths as relative to the directory of this executable. + std::filesystem::path executable_location = + flutter::GetExecutableDirectory(); + if (executable_location.empty()) { + std::cerr + << "Unable to find executable location to resolve resource paths." + << std::endl; + return nullptr; + } + assets_path = std::filesystem::path(executable_location) / assets_path; + icu_path = std::filesystem::path(executable_location) / icu_path; + } + std::string assets_path_string = assets_path.u8string(); + std::string icu_path_string = icu_path.u8string(); + FlutterProjectArgs args = {}; args.struct_size = sizeof(FlutterProjectArgs); - args.assets_path = assets_path; - args.icu_data_path = icu_data_path; + args.assets_path = assets_path_string.c_str(); + args.icu_data_path = icu_path_string.c_str(); args.command_line_argc = static_cast(argv.size()); args.command_line_argv = &argv[0]; args.platform_message_callback = @@ -132,18 +150,13 @@ static std::unique_ptr RunFlutterEngine( } FlutterDesktopViewControllerRef FlutterDesktopCreateViewController( - int initial_width, - int initial_height, - const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count) { + int width, + int height, + const FlutterDesktopEngineProperties& engine_properties) { FlutterDesktopViewControllerRef state = - flutter::Win32FlutterWindow::CreateWin32FlutterWindow(initial_width, - initial_height); + flutter::Win32FlutterWindow::CreateWin32FlutterWindow(width, height); - auto engine_state = RunFlutterEngine( - state->view.get(), assets_path, icu_data_path, arguments, argument_count); + auto engine_state = RunFlutterEngine(state->view.get(), engine_properties); if (!engine_state) { return nullptr; @@ -153,6 +166,26 @@ FlutterDesktopViewControllerRef FlutterDesktopCreateViewController( return state; } +FlutterDesktopViewControllerRef FlutterDesktopCreateViewControllerLegacy( + int initial_width, + int initial_height, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count) { + std::filesystem::path assets_path_fs = std::filesystem::u8path(assets_path); + std::filesystem::path icu_data_path_fs = + std::filesystem::u8path(icu_data_path); + FlutterDesktopEngineProperties engine_properties = {}; + engine_properties.assets_path = assets_path_fs.c_str(); + engine_properties.icu_data_path = icu_data_path_fs.c_str(); + engine_properties.switches = arguments; + engine_properties.switches_count = argument_count; + + return FlutterDesktopCreateViewController(initial_width, initial_height, + engine_properties); +} + uint64_t FlutterDesktopProcessMessages( FlutterDesktopViewControllerRef controller) { return controller->engine_state->task_runner->ProcessTasks().count(); @@ -191,12 +224,9 @@ UINT FlutterDesktopGetDpiForMonitor(HMONITOR monitor) { return flutter::GetDpiForMonitor(monitor); } -FlutterDesktopEngineRef FlutterDesktopRunEngine(const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count) { - auto engine = RunFlutterEngine(nullptr, assets_path, icu_data_path, arguments, - argument_count); +FlutterDesktopEngineRef FlutterDesktopRunEngine( + const FlutterDesktopEngineProperties& engine_properties) { + auto engine = RunFlutterEngine(nullptr, engine_properties); return engine.release(); } diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h index 7626be1239c35..7d9c4155ad32a 100644 --- a/shell/platform/windows/public/flutter_windows.h +++ b/shell/platform/windows/public/flutter_windows.h @@ -28,24 +28,48 @@ typedef struct FlutterDesktopView* FlutterDesktopViewRef; // Opaque reference to a Flutter engine instance. typedef struct FlutterDesktopEngineState* FlutterDesktopEngineRef; -// Creates a View running a Flutter Application. +// Properties for configuring a Flutter engine instance. +typedef struct { + // The path to the flutter_assets folder for the application to be run. + // This can either be an absolute path or a path relative to the directory + // containing the executable. + const wchar_t* assets_path; + + // The path to the icudtl.dat file for the version of Flutter you are using. + // This can either be an absolute path or a path relative to the directory + // containing the executable. + const wchar_t* icu_data_path; + + // The switches to pass to the Flutter engine. + // + // See: https://github.com/flutter/engine/blob/master/shell/common/switches.h + // for details. Not all arguments will apply to desktop. + const char** switches; + + // The number of elements in |switches|. + size_t switches_count; +} FlutterDesktopEngineProperties; + +// Creates a View with the given dimensions running a Flutter Application. // -// The |assets_path| is the path to the flutter_assets folder for the Flutter -// application to be run. |icu_data_path| is the path to the icudtl.dat file -// for the version of Flutter you are using. -// -// The |arguments| are passed to the Flutter engine. See: -// https://github.com/flutter/engine/blob/master/shell/common/switches.h for -// for details. Not all arguments will apply to desktop. +// This will set up and run an associated Flutter engine using the settings in +// |engine_properties|. // // Returns a null pointer in the event of an error. FLUTTER_EXPORT FlutterDesktopViewControllerRef -FlutterDesktopCreateViewController(int initial_width, - int initial_height, - const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count); +FlutterDesktopCreateViewController( + int width, + int height, + const FlutterDesktopEngineProperties& engine_properties); + +// DEPRECATED. Will be removed soon; switch to the version above. +FLUTTER_EXPORT FlutterDesktopViewControllerRef +FlutterDesktopCreateViewControllerLegacy(int initial_width, + int initial_height, + const char* assets_path, + const char* icu_data_path, + const char** arguments, + size_t argument_count); // Shuts down the engine instance associated with |controller|, and cleans up // associated state. @@ -88,20 +112,9 @@ FLUTTER_EXPORT UINT FlutterDesktopGetDpiForMonitor(HMONITOR monitor); // Runs an instance of a headless Flutter engine. // -// The |assets_path| is the path to the flutter_assets folder for the Flutter -// application to be run. |icu_data_path| is the path to the icudtl.dat file -// for the version of Flutter you are using. -// -// The |arguments| are passed to the Flutter engine. See: -// https://github.com/flutter/engine/blob/master/shell/common/switches.h for -// for details. Not all arguments will apply to desktop. -// // Returns a null pointer in the event of an error. -FLUTTER_EXPORT FlutterDesktopEngineRef -FlutterDesktopRunEngine(const char* assets_path, - const char* icu_data_path, - const char** arguments, - size_t argument_count); +FLUTTER_EXPORT FlutterDesktopEngineRef FlutterDesktopRunEngine( + const FlutterDesktopEngineProperties& engine_properties); // Shuts down the given engine instance. Returns true if the shutdown was // successful. |engine_ref| is no longer valid after this call. From af056a8de45515b22572d1cc4106e8ee42a8ec2e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 13:51:06 -0400 Subject: [PATCH 100/386] Roll src/third_party/skia 6f60a859c628..4098719375d8 (6 commits) (#17271) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 8aaaf83eaf55f..be8c1e9b61aa6 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6f60a859c628ccf4e97986782cf795574a7248d6', + 'skia_revision': '4098719375d865966c830c1b21182d8c0e7d6b27', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index ce1c8a013c960..2a861cbfbda23 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ee71919fe2c43e1dc6ea0b98b18c9841 +Signature: c7ffdc3920b7d981cffb29708ffdf9cb UNUSED LICENSES: From 9343c5ed28ad8a4ac8207d92f405026beb183170 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 23 Mar 2020 11:38:42 -0700 Subject: [PATCH 101/386] added missing mock to MockEngine for iOS unit tests (#17273) --- .../darwin/ios/framework/Source/FlutterViewControllerTest.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m index 09ef7baa023fb..403e4e3625311 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m @@ -27,6 +27,9 @@ @interface MockEngine : NSObject @end @implementation MockEngine +- (FlutterViewController*)viewController { + return nil; +} - (void)setViewController:(FlutterViewController*)viewController { // noop } From 6fef0d0a0be4a3bc0458372da7fd01c250997621 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 23 Mar 2020 12:57:15 -0700 Subject: [PATCH 102/386] Removed a text input trait that causes VoiceOver to be incorrect when tapping a text input. (#17203) --- .../ios/framework/Source/accessibility_text_entry.mm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm index 0bf0a6963606f..9fb441413b665 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm @@ -7,6 +7,8 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h" #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h" +static const UIAccessibilityTraits UIAccessibilityTraitUndocumentedEmptyLine = 0x800000000000; + @implementation FlutterInactiveTextInput { } @@ -284,8 +286,13 @@ - (UIAccessibilityTraits)accessibilityTraits { // Adding UIAccessibilityTraitKeyboardKey to the trait list so that iOS treats it like // a keyboard entry control, thus adding support for text editing features, such as // pinch to select text, and up/down fling to move cursor. - return [super accessibilityTraits] | [self textInputSurrogate].accessibilityTraits | - UIAccessibilityTraitKeyboardKey; + UIAccessibilityTraits results = [super accessibilityTraits] | + [self textInputSurrogate].accessibilityTraits | + UIAccessibilityTraitKeyboardKey; + // We remove an undocumented flag to get rid of a bug where single-tapping + // a text input field incorrectly says "empty line". + // See also: https://github.com/flutter/flutter/issues/52487 + return results & (~UIAccessibilityTraitUndocumentedEmptyLine); } #pragma mark - UITextInput overrides From 21f5d7f3cec0ff4ad4faae9a78a926870fc47ea6 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 16:06:02 -0400 Subject: [PATCH 103/386] Roll src/third_party/skia 4098719375d8..165b68ebb6ad (7 commits) (#17274) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index be8c1e9b61aa6..3588ef2d82b82 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4098719375d865966c830c1b21182d8c0e7d6b27', + 'skia_revision': '165b68ebb6ad21adae9f7a099ee0f78de93e1424', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2a861cbfbda23..56a39642cd5f9 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c7ffdc3920b7d981cffb29708ffdf9cb +Signature: e1dd415a23c0d320e1c9a93bebaa9540 UNUSED LICENSES: From 4c41b1406493f8a33e5034e68192f929f1316bdd Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Mon, 23 Mar 2020 13:22:10 -0700 Subject: [PATCH 104/386] [web] Introduce js interop to enable experimental flags on web (#17099) --- ci/licenses_golden/licenses_flutter | 1 + lib/web_ui/lib/src/engine.dart | 3 + .../lib/src/engine/text/measurement.dart | 9 +-- .../lib/src/engine/web_experiments.dart | 53 ++++++++++++ lib/web_ui/test/canvas_test.dart | 4 + .../test/engine/web_experiments_test.dart | 80 +++++++++++++++++++ .../test/golden_tests/engine/scuba.dart | 8 +- .../engine/text_overflow_golden_test.dart | 6 +- lib/web_ui/test/paragraph_builder_test.dart | 5 ++ lib/web_ui/test/paragraph_test.dart | 24 +++--- 10 files changed, 168 insertions(+), 25 deletions(-) create mode 100644 lib/web_ui/lib/src/engine/web_experiments.dart create mode 100644 lib/web_ui/test/engine/web_experiments_test.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c2a8ee725502c..1f029bb647d73 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -495,6 +495,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/text_editing/text_editing.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/util.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/validators.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/vector_math.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/web_experiments.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/window.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/annotations.dart FILE: ../../../flutter/lib/web_ui/lib/src/ui/canvas.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 00b90d9bb4b9d..04e688ff090e7 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -117,6 +117,7 @@ part 'engine/text_editing/text_editing.dart'; part 'engine/util.dart'; part 'engine/validators.dart'; part 'engine/vector_math.dart'; +part 'engine/web_experiments.dart'; part 'engine/window.dart'; bool _engineInitialized = false; @@ -161,6 +162,8 @@ void webOnlyInitializeEngine() { // initialize framework bindings. domRenderer; + WebExperiments.ensureInitialized(); + bool waitingForAnimation = false; ui.webOnlyScheduleFrameCallback = () { // We're asked to schedule a frame and call `frameHandler` when the frame diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index 3781ff6412ef5..30ae994538c1a 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -187,13 +187,6 @@ abstract class TextMeasurementService { static TextMeasurementService get canvasInstance => CanvasTextMeasurementService.instance; - /// Whether the new experimental implementation of canvas-based text - /// measurement is enabled or not. - /// - /// This is only used for testing at the moment. Once the implementation is - /// complete and production-ready, we'll get rid of this flag. - static bool enableExperimentalCanvasImplementation = const bool.fromEnvironment('FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT', defaultValue: false); - /// Gets the appropriate [TextMeasurementService] instance for the given /// [paragraph]. static TextMeasurementService forParagraph(ui.Paragraph paragraph) { @@ -206,7 +199,7 @@ abstract class TextMeasurementService { // Skip using canvas measurements until the iframe becomes visible. // see: https://github.com/flutter/flutter/issues/36341 if (!window.physicalSize.isEmpty && - enableExperimentalCanvasImplementation && + WebExperiments.instance.useCanvasText && _canUseCanvasMeasurement(paragraph)) { return canvasInstance; } diff --git a/lib/web_ui/lib/src/engine/web_experiments.dart b/lib/web_ui/lib/src/engine/web_experiments.dart new file mode 100644 index 0000000000000..2e2050938e8df --- /dev/null +++ b/lib/web_ui/lib/src/engine/web_experiments.dart @@ -0,0 +1,53 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of engine; + +/// A bag of all experiment flags in the web engine. +/// +/// This class also handles platform messages that can be sent to enable/disable +/// certain experiments at runtime without the need to access engine internals. +class WebExperiments { + WebExperiments._() { + js.context['_flutter_internal_update_experiment'] = updateExperiment; + registerHotRestartListener(() { + js.context['_flutter_internal_update_experiment'] = null; + }); + } + + static WebExperiments ensureInitialized() { + if (WebExperiments.instance == null) { + WebExperiments.instance = WebExperiments._(); + } + return WebExperiments.instance; + } + + static WebExperiments instance; + + /// Experiment flag for using canvas-based text measurement. + bool get useCanvasText => _useCanvasText ?? false; + set useCanvasText(bool enabled) { + _useCanvasText = enabled; + } + + bool _useCanvasText = const bool.fromEnvironment( + 'FLUTTER_WEB_USE_EXPERIMENTAL_CANVAS_TEXT', + defaultValue: null, + ); + + /// Reset all experimental flags to their default values. + void reset() { + _useCanvasText = null; + } + + /// Used to enable/disable experimental flags in the web engine. + void updateExperiment(String name, bool enabled) { + switch (name) { + case 'useCanvasText': + _useCanvasText = enabled; + break; + } + } +} diff --git a/lib/web_ui/test/canvas_test.dart b/lib/web_ui/test/canvas_test.dart index 363204a120d6e..3a63ee87085d7 100644 --- a/lib/web_ui/test/canvas_test.dart +++ b/lib/web_ui/test/canvas_test.dart @@ -11,6 +11,10 @@ import 'package:test/test.dart'; import 'mock_engine_canvas.dart'; void main() { + setUpAll(() { + WebExperiments.ensureInitialized(); + }); + group('EngineCanvas', () { MockEngineCanvas mockCanvas; ui.Paragraph paragraph; diff --git a/lib/web_ui/test/engine/web_experiments_test.dart b/lib/web_ui/test/engine/web_experiments_test.dart new file mode 100644 index 0000000000000..034cbba04b945 --- /dev/null +++ b/lib/web_ui/test/engine/web_experiments_test.dart @@ -0,0 +1,80 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; + +void main() { + setUp(() { + WebExperiments.ensureInitialized(); + }); + + tearDown(() { + WebExperiments.instance.reset(); + }); + + test('default web experiment values', () { + expect(WebExperiments.instance.useCanvasText, false); + }); + + test('can turn on/off web experiments', () { + WebExperiments.instance.updateExperiment('useCanvasText', true); + expect(WebExperiments.instance.useCanvasText, true); + + WebExperiments.instance.updateExperiment('useCanvasText', false); + expect(WebExperiments.instance.useCanvasText, false); + + WebExperiments.instance.updateExperiment('useCanvasText', null); + // Goes back to default value. + expect(WebExperiments.instance.useCanvasText, false); + }); + + test('ignores unknown experiments', () { + expect(WebExperiments.instance.useCanvasText, false); + WebExperiments.instance.updateExperiment('foobarbazqux', true); + expect(WebExperiments.instance.useCanvasText, false); + WebExperiments.instance.updateExperiment('foobarbazqux', false); + expect(WebExperiments.instance.useCanvasText, false); + }); + + test('can reset web experiments', () { + WebExperiments.instance.updateExperiment('useCanvasText', true); + WebExperiments.instance.reset(); + expect(WebExperiments.instance.useCanvasText, false); + + WebExperiments.instance.updateExperiment('useCanvasText', true); + WebExperiments.instance.updateExperiment('foobarbazqux', true); + WebExperiments.instance.reset(); + expect(WebExperiments.instance.useCanvasText, false); + }); + + test('js interop also works', () { + expect(WebExperiments.instance.useCanvasText, false); + + expect(() => jsUpdateExperiment('useCanvasText', true), returnsNormally); + expect(WebExperiments.instance.useCanvasText, true); + + expect(() => jsUpdateExperiment('useCanvasText', null), returnsNormally); + expect(WebExperiments.instance.useCanvasText, false); + }); + + test('js interop throws on wrong type', () { + expect(() => jsUpdateExperiment(123, true), throwsA(anything)); + expect(() => jsUpdateExperiment('foo', 123), throwsA(anything)); + expect(() => jsUpdateExperiment('foo', 'bar'), throwsA(anything)); + expect(() => jsUpdateExperiment(false, 'foo'), throwsA(anything)); + }); +} + +void jsUpdateExperiment(dynamic name, dynamic enabled) { + js_util.callMethod( + html.window, + '_flutter_internal_update_experiment', + [name, enabled], + ); +} diff --git a/lib/web_ui/test/golden_tests/engine/scuba.dart b/lib/web_ui/test/golden_tests/engine/scuba.dart index 421f4ae758be6..6ae5739577be5 100644 --- a/lib/web_ui/test/golden_tests/engine/scuba.dart +++ b/lib/web_ui/test/golden_tests/engine/scuba.dart @@ -71,7 +71,7 @@ class EngineScubaTester { sceneElement.append(canvas.rootElement); html.document.body.append(sceneElement); String screenshotName = '${fileName}_${canvas.runtimeType}'; - if (TextMeasurementService.enableExperimentalCanvasImplementation) { + if (WebExperiments.instance.useCanvasText) { screenshotName += '+canvas_measurement'; } await diffScreenshot( @@ -96,18 +96,20 @@ void testEachCanvas(String description, CanvasTest body, test('$description (bitmap)', () { try { TextMeasurementService.initialize(rulerCacheCapacity: 2); + WebExperiments.instance.useCanvasText = false; return body(BitmapCanvas(bounds)); } finally { + WebExperiments.instance.useCanvasText = null; TextMeasurementService.clearCache(); } }); test('$description (bitmap + canvas measurement)', () async { try { TextMeasurementService.initialize(rulerCacheCapacity: 2); - TextMeasurementService.enableExperimentalCanvasImplementation = true; + WebExperiments.instance.useCanvasText = true; await body(BitmapCanvas(bounds)); } finally { - TextMeasurementService.enableExperimentalCanvasImplementation = false; + WebExperiments.instance.useCanvasText = null; TextMeasurementService.clearCache(); } }); diff --git a/lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart index 167921f337f5b..507be4fd33df9 100644 --- a/lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/text_overflow_golden_test.dart @@ -5,7 +5,7 @@ // @dart = 2.6 import 'dart:async'; -import 'package:ui/ui.dart'; +import 'package:ui/ui.dart' hide window; import 'package:ui/src/engine.dart'; import 'scuba.dart'; @@ -89,7 +89,7 @@ void main() async { offset = offset.translate(0, p.height + 10); // Only the first line is rendered with an ellipsis. - if (!TextMeasurementService.enableExperimentalCanvasImplementation) { + if (!WebExperiments.instance.useCanvasText) { // This is now correct with the canvas-based measurement, so we shouldn't // print the "(wrong)" warning. p = warning('(wrong)'); @@ -106,7 +106,7 @@ void main() async { // Only the first two lines are rendered and the ellipsis appears on the 2nd // line. - if (!TextMeasurementService.enableExperimentalCanvasImplementation) { + if (!WebExperiments.instance.useCanvasText) { // This is now correct with the canvas-based measurement, so we shouldn't // print the "(wrong)" warning. p = warning('(wrong)'); diff --git a/lib/web_ui/test/paragraph_builder_test.dart b/lib/web_ui/test/paragraph_builder_test.dart index 08ac0d778fbf2..54a3bcf53b046 100644 --- a/lib/web_ui/test/paragraph_builder_test.dart +++ b/lib/web_ui/test/paragraph_builder_test.dart @@ -3,11 +3,16 @@ // found in the LICENSE file. // @dart = 2.6 +import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart'; import 'package:test/test.dart'; void main() { + setUpAll(() { + WebExperiments.ensureInitialized(); + }); + test('Should be able to build and layout a paragraph', () { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); builder.addText('Hello'); diff --git a/lib/web_ui/test/paragraph_test.dart b/lib/web_ui/test/paragraph_test.dart index 29d52b830218f..c3432a00bed1f 100644 --- a/lib/web_ui/test/paragraph_test.dart +++ b/lib/web_ui/test/paragraph_test.dart @@ -4,7 +4,7 @@ // @dart = 2.6 import 'package:ui/src/engine.dart'; -import 'package:ui/ui.dart'; +import 'package:ui/ui.dart' hide window; import 'package:test/test.dart'; @@ -12,18 +12,20 @@ void testEachMeasurement(String description, VoidCallback body, {bool skip}) { test('$description (dom measurement)', () async { try { TextMeasurementService.initialize(rulerCacheCapacity: 2); + WebExperiments.instance.useCanvasText = false; return body(); } finally { + WebExperiments.instance.useCanvasText = null; TextMeasurementService.clearCache(); } }, skip: skip); test('$description (canvas measurement)', () async { try { TextMeasurementService.initialize(rulerCacheCapacity: 2); - TextMeasurementService.enableExperimentalCanvasImplementation = true; + WebExperiments.instance.useCanvasText = true; return body(); } finally { - TextMeasurementService.enableExperimentalCanvasImplementation = false; + WebExperiments.instance.useCanvasText = null; TextMeasurementService.clearCache(); } }, skip: skip); @@ -184,7 +186,7 @@ void main() async { test('getPositionForOffset multi-line', () { // [Paragraph.getPositionForOffset] for multi-line text doesn't work well // with dom-based measurement. - TextMeasurementService.enableExperimentalCanvasImplementation = true; + WebExperiments.instance.useCanvasText = true; TextMeasurementService.initialize(rulerCacheCapacity: 2); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( @@ -280,11 +282,11 @@ void main() async { ); TextMeasurementService.clearCache(); - TextMeasurementService.enableExperimentalCanvasImplementation = false; + WebExperiments.instance.useCanvasText = null; }); test('getPositionForOffset multi-line centered', () { - TextMeasurementService.enableExperimentalCanvasImplementation = true; + WebExperiments.instance.useCanvasText = true; TextMeasurementService.initialize(rulerCacheCapacity: 2); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( @@ -387,7 +389,7 @@ void main() async { ); TextMeasurementService.clearCache(); - TextMeasurementService.enableExperimentalCanvasImplementation = false; + WebExperiments.instance.useCanvasText = null; }); testEachMeasurement('getBoxesForRange returns a box', () { @@ -782,7 +784,7 @@ void main() async { test('longestLine', () { // [Paragraph.longestLine] is only supported by canvas-based measurement. - TextMeasurementService.enableExperimentalCanvasImplementation = true; + WebExperiments.instance.useCanvasText = true; TextMeasurementService.initialize(rulerCacheCapacity: 2); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( @@ -797,7 +799,7 @@ void main() async { expect(paragraph.longestLine, 50.0); TextMeasurementService.clearCache(); - TextMeasurementService.enableExperimentalCanvasImplementation = false; + WebExperiments.instance.useCanvasText = null; }); testEachMeasurement('getLineBoundary (single-line)', () { @@ -824,7 +826,7 @@ void main() async { test('getLineBoundary (multi-line)', () { // [Paragraph.getLineBoundary] for multi-line paragraphs is only supported // by canvas-based measurement. - TextMeasurementService.enableExperimentalCanvasImplementation = true; + WebExperiments.instance.useCanvasText = true; TextMeasurementService.initialize(rulerCacheCapacity: 2); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( @@ -867,7 +869,7 @@ void main() async { } TextMeasurementService.clearCache(); - TextMeasurementService.enableExperimentalCanvasImplementation = false; + WebExperiments.instance.useCanvasText = null; }); testEachMeasurement('width should be a whole integer', () { From c490cb9be1783c1f429c37ef45d2c35e5bd06ef9 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 18:26:01 -0400 Subject: [PATCH 105/386] Roll src/third_party/skia 165b68ebb6ad..3c358420df4a (5 commits) (#17277) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 3588ef2d82b82..a37c5e7a64dd5 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '165b68ebb6ad21adae9f7a099ee0f78de93e1424', + 'skia_revision': '3c358420df4acd0e4efb57fffc7c1a28e0488da9', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 56a39642cd5f9..79349ecfab509 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: e1dd415a23c0d320e1c9a93bebaa9540 +Signature: 5ae073fe9ba4767aae57b5f1ca3988b7 UNUSED LICENSES: @@ -3906,6 +3906,8 @@ FILE: ../../../third_party/skia/src/gpu/GrManagedResource.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3D12.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCommandList.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCommandList.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.cpp From ad8ccf47b17eeb4d80bd08a4bcf05ea98bdb19fb Mon Sep 17 00:00:00 2001 From: mikejurka Date: Mon, 23 Mar 2020 16:38:07 -0700 Subject: [PATCH 106/386] [Fuchsia] Move physical shape layer compositing to Flutter (#17005) * [fuchsia] Add labels to Scenic nodes. * [fuchsia] Skip creating Scenic nodes for identity Transforms. * [fuchsia] Assign elevation to Scenic nodes based on paint order. * [fuchsia] Create Scenic OpacityNodes at leaf nodes. * [fuchsia] Composite PhysicalShapeLayers using Skia, except when they need to float above child views. In that case, they will still need to be pulled into separate Scenic nodes to be composited on top of the child view[s]. * [fuchsia] Add tests for Fuchsia-specific layer behavior. Inspect commands going to Scenic and make sure they match what is expected. Also, restructure code to need less member variables, and other cleanups based on review feedback. --- ci/licenses_golden/licenses_flutter | 1 + flow/BUILD.gn | 9 + flow/layers/child_scene_layer.cc | 5 +- flow/layers/fuchsia_layer_unittests.cc | 764 ++++++++++++++++++ flow/layers/layer.h | 5 + flow/layers/layer_tree.cc | 2 +- flow/layers/opacity_layer.cc | 51 +- flow/layers/opacity_layer.h | 1 - flow/layers/physical_shape_layer.cc | 93 ++- flow/layers/physical_shape_layer.h | 3 + flow/layers/physical_shape_layer_unittests.cc | 12 - flow/layers/transform_layer.cc | 8 +- flow/scene_update_context.cc | 50 +- flow/scene_update_context.h | 37 +- flow/view_holder.cc | 10 +- flow/view_holder.h | 2 + .../platform/fuchsia/flutter/vulkan_surface.h | 7 +- .../fuchsia/flutter/vulkan_surface_pool.h | 3 +- .../fuchsia/flutter/vulkan_surface_producer.h | 2 +- 19 files changed, 935 insertions(+), 130 deletions(-) create mode 100644 flow/layers/fuchsia_layer_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1f029bb647d73..135e7a141dd5f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -49,6 +49,7 @@ FILE: ../../../flutter/flow/layers/color_filter_layer_unittests.cc FILE: ../../../flutter/flow/layers/container_layer.cc FILE: ../../../flutter/flow/layers/container_layer.h FILE: ../../../flutter/flow/layers/container_layer_unittests.cc +FILE: ../../../flutter/flow/layers/fuchsia_layer_unittests.cc FILE: ../../../flutter/flow/layers/image_filter_layer.cc FILE: ../../../flutter/flow/layers/image_filter_layer.h FILE: ../../../flutter/flow/layers/image_filter_layer_unittests.cc diff --git a/flow/BUILD.gn b/flow/BUILD.gn index 14ceec4dadc5c..e25b5dc8957a3 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -88,6 +88,7 @@ source_set("flow") { if (using_fuchsia_sdk) { public_deps += [ + "$fuchsia_sdk_root/fidl:fuchsia.ui.app", "$fuchsia_sdk_root/fidl:fuchsia.ui.gfx", "$fuchsia_sdk_root/pkg:scenic_cpp", ] @@ -159,6 +160,10 @@ executable("flow_unittests") { "texture_unittests.cc", ] + if (is_fuchsia) { + sources += [ "layers/fuchsia_layer_unittests.cc" ] + } + deps = [ ":flow", ":flow_fixtures", @@ -170,6 +175,10 @@ executable("flow_unittests") { "//third_party/googletest:gtest", "//third_party/skia", ] + + if (is_fuchsia) { + deps += [ "//build/fuchsia/pkg:sys_cpp_testing" ] + } } if (is_fuchsia) { diff --git a/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc index 4a5358053928f..4e533130c1d4b 100644 --- a/flow/layers/child_scene_layer.cc +++ b/flow/layers/child_scene_layer.cc @@ -20,6 +20,7 @@ ChildSceneLayer::ChildSceneLayer(zx_koid_t layer_id, void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "ChildSceneLayer::Preroll"); set_needs_system_composite(true); + context->child_scene_layer_exists_below = true; // An alpha "hole punch" is required if the frame behind us is not opaque. if (!context->is_opaque) { @@ -49,7 +50,9 @@ void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) { auto* view_holder = ViewHolder::FromId(layer_id_); FML_DCHECK(view_holder); - view_holder->UpdateScene(context, offset_, size_, hit_testable_); + view_holder->UpdateScene(context, offset_, size_, + SkScalarRoundToInt(context.alphaf() * 255), + hit_testable_); } } // namespace flutter diff --git a/flow/layers/fuchsia_layer_unittests.cc b/flow/layers/fuchsia_layer_unittests.cc new file mode 100644 index 0000000000000..2a859226ab5ed --- /dev/null +++ b/flow/layers/fuchsia_layer_unittests.cc @@ -0,0 +1,764 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flutter/flow/layers/child_scene_layer.h" +#include "flutter/flow/layers/container_layer.h" +#include "flutter/flow/layers/opacity_layer.h" +#include "flutter/flow/layers/physical_shape_layer.h" +#include "flutter/flow/view_holder.h" +#include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h" +#include "flutter/fml/task_runner.h" + +namespace flutter { +namespace testing { + +using FuchsiaLayerTest = ::testing::Test; + +class MockSession : public fuchsia::ui::scenic::testing::Session_TestBase { + public: + MockSession() : binding_(this) {} + + void NotImplemented_(const std::string& name) final {} + + void Bind(fidl::InterfaceRequest<::fuchsia::ui::scenic::Session> request, + ::fuchsia::ui::scenic::SessionListenerPtr listener) { + binding_.Bind(std::move(request)); + listener_ = std::move(listener); + } + + static std::string Vec3ValueToString(fuchsia::ui::gfx::Vector3Value value) { + return "{" + std::to_string(value.value.x) + ", " + + std::to_string(value.value.y) + ", " + + std::to_string(value.value.z) + "}"; + } + + static std::string GfxCreateResourceCmdToString( + const fuchsia::ui::gfx::CreateResourceCmd& cmd) { + std::string id = " id: " + std::to_string(cmd.id); + switch (cmd.resource.Which()) { + case fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle: + return "Rectangle" + id; + case fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle: + return "RoundedRectangle" + id; + case fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder: + return "ViewHolder" + id; + case fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode: + return "OpacityNode" + id; + case fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode: + return "EntityNode" + id; + case fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode: + return "ShapeNode" + id; + case fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial: + return "Material" + id; + case fuchsia::ui::gfx::ResourceArgs::Tag::kImage: + return "Image" + id + ", memory_id: " + + std::to_string(cmd.resource.image().memory_id) + + ", memory_offset: " + + std::to_string(cmd.resource.image().memory_offset); + default: + return "Unhandled CreateResource command" + + std::to_string(cmd.resource.Which()); + } + } + + static std::string GfxCmdToString(const fuchsia::ui::gfx::Command& cmd) { + switch (cmd.Which()) { + case fuchsia::ui::gfx::Command::Tag::kCreateResource: + return "CreateResource: " + + GfxCreateResourceCmdToString(cmd.create_resource()); + case fuchsia::ui::gfx::Command::Tag::kReleaseResource: + return "ReleaseResource id: " + + std::to_string(cmd.release_resource().id); + case fuchsia::ui::gfx::Command::Tag::kAddChild: + return "AddChild id: " + std::to_string(cmd.add_child().node_id) + + " child_id: " + std::to_string(cmd.add_child().child_id); + case fuchsia::ui::gfx::Command::Tag::kSetTranslation: + return "SetTranslation id: " + + std::to_string(cmd.set_translation().id) + + " value: " + Vec3ValueToString(cmd.set_translation().value); + case fuchsia::ui::gfx::Command::Tag::kSetScale: + return "SetScale id: " + std::to_string(cmd.set_scale().id) + + " value: " + Vec3ValueToString(cmd.set_translation().value); + case fuchsia::ui::gfx::Command::Tag::kSetRotation: + return "SetRotation id: " + std::to_string(cmd.set_rotation().id); + case fuchsia::ui::gfx::Command::Tag::kSetOpacity: + return "SetOpacity id: " + std::to_string(cmd.set_opacity().node_id) + + ", opacity: " + std::to_string(cmd.set_opacity().opacity); + case fuchsia::ui::gfx::Command::Tag::kSetColor: + return "SetColor id: " + std::to_string(cmd.set_color().material_id) + + ", rgba: (" + std::to_string(cmd.set_color().color.value.red) + + ", " + std::to_string(cmd.set_color().color.value.green) + ", " + + std::to_string(cmd.set_color().color.value.blue) + ", " + + std::to_string(cmd.set_color().color.value.alpha) + ")"; + case fuchsia::ui::gfx::Command::Tag::kSetLabel: + return "SetLabel id: " + std::to_string(cmd.set_label().id) + " " + + cmd.set_label().label; + case fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior: + return "SetHitTestBehavior node_id: " + + std::to_string(cmd.set_hit_test_behavior().node_id); + case fuchsia::ui::gfx::Command::Tag::kSetClipPlanes: + return "SetClipPlanes node_id: " + + std::to_string(cmd.set_clip_planes().node_id); + case fuchsia::ui::gfx::Command::Tag::kSetShape: + return "SetShape node_id: " + std::to_string(cmd.set_shape().node_id) + + ", shape_id: " + std::to_string(cmd.set_shape().shape_id); + case fuchsia::ui::gfx::Command::Tag::kSetMaterial: + return "SetMaterial node_id: " + + std::to_string(cmd.set_material().node_id) + ", material_id: " + + std::to_string(cmd.set_material().material_id); + case fuchsia::ui::gfx::Command::Tag::kSetTexture: + return "SetTexture material_id: " + + std::to_string(cmd.set_texture().material_id) + + ", texture_id: " + std::to_string(cmd.set_texture().texture_id); + + default: + return "Unhandled gfx command" + std::to_string(cmd.Which()); + } + } + + static std::string ScenicCmdToString( + const fuchsia::ui::scenic::Command& cmd) { + if (cmd.Which() != fuchsia::ui::scenic::Command::Tag::kGfx) { + return "Unhandled non-gfx command: " + std::to_string(cmd.Which()); + } + return GfxCmdToString(cmd.gfx()); + } + + // |fuchsia::ui::scenic::Session| + void Enqueue(std::vector cmds) override { + for (const auto& cmd : cmds) { + num_enqueued_commands_++; + EXPECT_FALSE(expected_.empty()) + << "Received more commands than expected; command: <" + << ScenicCmdToString(cmd) + << ">, num_enqueued_commands: " << num_enqueued_commands_; + if (!expected_.empty()) { + EXPECT_TRUE(AreCommandsEqual(expected_.front(), cmd)) + << "actual command: <" << ScenicCmdToString(cmd) + << ">, expected command: <" << ScenicCmdToString(expected_.front()) + << ">, num_enqueued_commands: " << num_enqueued_commands_; + expected_.pop_front(); + } + } + } + + void SetExpectedCommands(std::vector gfx_cmds) { + std::deque scenic_commands; + for (auto it = gfx_cmds.begin(); it != gfx_cmds.end(); it++) { + scenic_commands.push_back(scenic::NewCommand(std::move((*it)))); + } + expected_ = std::move(scenic_commands); + num_enqueued_commands_ = 0; + } + + size_t num_enqueued_commands() { return num_enqueued_commands_; } + + private: + static bool IsGfxCommand(const fuchsia::ui::scenic::Command& cmd, + fuchsia::ui::gfx::Command::Tag tag) { + return cmd.Which() == fuchsia::ui::scenic::Command::Tag::kGfx && + cmd.gfx().Which() == tag; + } + + static bool IsCreateResourceCommand(const fuchsia::ui::scenic::Command& cmd, + fuchsia::ui::gfx::ResourceArgs::Tag tag) { + return IsGfxCommand(cmd, fuchsia::ui::gfx::Command::Tag::kCreateResource) && + cmd.gfx().create_resource().resource.Which() == tag; + } + + static bool AreCommandsEqual(const fuchsia::ui::scenic::Command& command1, + const fuchsia::ui::scenic::Command& command2) { + // For CreateViewHolderCommand, just compare the id and ignore the + // view_holder_token. + if (IsCreateResourceCommand( + command1, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder)) { + return IsCreateResourceCommand( + command2, fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder) && + command1.gfx().create_resource().id == + command2.gfx().create_resource().id; + } + // For CreateImageCommand, just compare the id and memory_id. + if (IsCreateResourceCommand(command1, + fuchsia::ui::gfx::ResourceArgs::Tag::kImage)) { + return IsCreateResourceCommand( + command2, fuchsia::ui::gfx::ResourceArgs::Tag::kImage) && + command1.gfx().create_resource().id == + command2.gfx().create_resource().id && + command1.gfx().create_resource().resource.image().memory_id == + command2.gfx().create_resource().resource.image().memory_id; + } + // For SetHitTestBehaviorCommand, just compare the node_id. + if (IsGfxCommand(command1, + fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior)) { + return IsGfxCommand( + command2, + fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior) && + command1.gfx().set_hit_test_behavior().node_id == + command2.gfx().set_hit_test_behavior().node_id; + } + // For SetHitTestBehaviorCommand, just compare the node_id. + if (IsGfxCommand(command1, + fuchsia::ui::gfx::Command::Tag::kSetClipPlanes)) { + return IsGfxCommand(command2, + fuchsia::ui::gfx::Command::Tag::kSetClipPlanes) && + command1.gfx().set_clip_planes().node_id == + command2.gfx().set_clip_planes().node_id; + } + return fidl::Equals(command1, command2); + } + + std::deque expected_; + size_t num_enqueued_commands_ = 0; + fidl::Binding binding_; + fuchsia::ui::scenic::SessionListenerPtr listener_; +}; + +class MockSurfaceProducerSurface + : public SceneUpdateContext::SurfaceProducerSurface { + public: + MockSurfaceProducerSurface(scenic::Session* session, const SkISize& size) + : image_(session, 0, 0, {}), size_(size) {} + + size_t AdvanceAndGetAge() override { return 0; } + + bool FlushSessionAcquireAndReleaseEvents() override { return false; } + + bool IsValid() const override { return false; } + + SkISize GetSize() const override { return size_; } + + void SignalWritesFinished( + const std::function& on_writes_committed) override {} + + scenic::Image* GetImage() override { return &image_; }; + + sk_sp GetSkiaSurface() const override { return nullptr; }; + + private: + scenic::Image image_; + SkISize size_; +}; + +class MockSurfaceProducer : public SceneUpdateContext::SurfaceProducer { + public: + MockSurfaceProducer(scenic::Session* session) : session_(session) {} + std::unique_ptr ProduceSurface( + const SkISize& size, + const LayerRasterCacheKey& layer_key, + std::unique_ptr entity_node) override { + return std::make_unique(session_, size); + } + + // Query a retained entity node (owned by a retained surface) for retained + // rendering. + bool HasRetainedNode(const LayerRasterCacheKey& key) const override { + return false; + } + + scenic::EntityNode* GetRetainedNode(const LayerRasterCacheKey& key) override { + return nullptr; + } + + void SubmitSurface(std::unique_ptr + surface) override {} + + private: + scenic::Session* session_; +}; + +struct TestContext { + // Message loop. + fml::RefPtr loop; + fml::RefPtr task_runner; + + // Session. + MockSession mock_session; + fidl::InterfaceRequest listener_request; + std::unique_ptr session; + + // SceneUpdateContext. + std::unique_ptr mock_surface_producer; + std::unique_ptr scene_update_context; + + // PrerollContext. + MutatorsStack unused_stack; + const Stopwatch unused_stopwatch; + TextureRegistry unused_texture_registry; + std::unique_ptr preroll_context; +}; + +std::unique_ptr InitTest() { + std::unique_ptr context = std::make_unique(); + + // Init message loop. + context->loop = fml::MakeRefCounted(); + context->task_runner = fml::MakeRefCounted(context->loop); + + // Init Session. + fuchsia::ui::scenic::SessionPtr session_ptr; + fuchsia::ui::scenic::SessionListenerPtr listener; + context->listener_request = listener.NewRequest(); + context->mock_session.Bind(session_ptr.NewRequest(), std::move(listener)); + context->session = std::make_unique(std::move(session_ptr)); + + // Init SceneUpdateContext. + context->mock_surface_producer = + std::make_unique(context->session.get()); + context->scene_update_context = std::make_unique( + context->session.get(), context->mock_surface_producer.get()); + context->scene_update_context->set_metrics( + fidl::MakeOptional(fuchsia::ui::gfx::Metrics{1.f, 1.f, 1.f})); + + // Init PrerollContext. + context->preroll_context = std::unique_ptr(new PrerollContext{ + nullptr, // raster_cache (don't consult the cache) + nullptr, // gr_context (used for the raster cache) + nullptr, // external view embedder + context->unused_stack, // mutator stack + nullptr, // SkColorSpace* dst_color_space + kGiantRect, // SkRect cull_rect + false, // layer reads from surface + context->unused_stopwatch, // frame time (dont care) + context->unused_stopwatch, // engine time (dont care) + context->unused_texture_registry, // texture registry (not + // supported) + false, // checkerboard_offscreen_layers + 100.f, // maximum depth allowed for rendering + 1.f // ratio between logical and physical + }); + + return context; +} + +zx_koid_t GetChildLayerId() { + static zx_koid_t sChildLayerId = 17324; + return sChildLayerId++; +} + +class AutoDestroyChildLayerId { + public: + AutoDestroyChildLayerId(zx_koid_t id) : id_(id) {} + ~AutoDestroyChildLayerId() { ViewHolder::Destroy(id_); } + + private: + zx_koid_t id_; +}; + +// Create a hierarchy with PhysicalShapeLayers and ChildSceneLayers, and +// inspect the commands sent to Scenic. +// +// +// What we expect: +// +// The Scenic elevations of the PhysicalShapeLayers are monotically +// increasing, even though the elevations we gave them when creating them are +// decreasing. The two should not have any correlation; we're merely mirror +// the paint order using Scenic elevation. +// +// PhysicalShapeLayers created before/below a ChildView do not get their own +// node; PhysicalShapeLayers created afterward do. +// +// Nested PhysicalShapeLayers are collapsed. +TEST_F(FuchsiaLayerTest, PhysicalShapeLayersAndChildSceneLayers) { + auto test_context = InitTest(); + + // Root. + auto root = std::make_shared(); + SkPath path; + path.addRect(SkRect::MakeWH(10.f, 10.f)); + + // Child #1: PhysicalShapeLayer. + auto physical_shape1 = std::make_shared( + /*color=*/SK_ColorCYAN, + /*shadow_color=*/SK_ColorBLACK, + /*elevation*/ 23.f, path, Clip::antiAlias); + root->Add(physical_shape1); + + // Child #2: ChildSceneLayer. + const zx_koid_t kChildLayerId1 = GetChildLayerId(); + auto [unused_view_token1, unused_view_holder_token1] = + scenic::ViewTokenPair::New(); + ViewHolder::Create(kChildLayerId1, test_context->task_runner, + std::move(unused_view_holder_token1), + /*bind_callback=*/[](scenic::ResourceId id) {}); + // Will destroy only when we go out of scope (i.e. end of the test). + AutoDestroyChildLayerId auto_destroy1(kChildLayerId1); + auto child_view1 = std::make_shared( + kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10), + /*hit_testable=*/false); + root->Add(child_view1); + + // Child #3: PhysicalShapeLayer + auto physical_shape2 = std::make_shared( + /*color=*/SK_ColorCYAN, + /*shadow_color=*/SK_ColorBLACK, + /*elevation*/ 21.f, path, Clip::antiAlias); + root->Add(physical_shape2); + + // Grandchild (child of #3): PhysicalShapeLayer + auto physical_shape3 = std::make_shared( + /*color=*/SK_ColorCYAN, + /*shadow_color=*/SK_ColorBLACK, + /*elevation*/ 19.f, path, Clip::antiAlias); + physical_shape2->Add(physical_shape3); + + // Child #4: ChildSceneLayer + const zx_koid_t kChildLayerId2 = GetChildLayerId(); + auto [unused_view_token2, unused_view_holder_token2] = + scenic::ViewTokenPair::New(); + ViewHolder::Create(kChildLayerId2, test_context->task_runner, + std::move(unused_view_holder_token2), + /*bind_callback=*/[](scenic::ResourceId id) {}); + // Will destroy only when we go out of scope (i.e. end of the test). + AutoDestroyChildLayerId auto_destroy2(kChildLayerId2); + auto child_view2 = std::make_shared( + kChildLayerId2, SkPoint::Make(1, 1), SkSize::Make(10, 10), + /*hit_testable=*/false); + root->Add(child_view2); + + // Child #5: PhysicalShapeLayer + auto physical_shape4 = std::make_shared( + /*color=*/SK_ColorCYAN, + /*shadow_color=*/SK_ColorBLACK, + /*elevation*/ 17.f, path, Clip::antiAlias); + root->Add(physical_shape4); + + // Preroll. + root->Preroll(test_context->preroll_context.get(), SkMatrix()); + + // Create another frame to be the "real" root. Required because + // UpdateScene() traversal expects there to already be a top node. + SceneUpdateContext::Frame frame(*(test_context->scene_update_context), + SkRRect::MakeRect(SkRect::MakeWH(100, 100)), + SK_ColorTRANSPARENT, SK_AlphaOPAQUE, + "fuchsia test root"); + + // Submit the list of command we will expect Scenic to see. + // + // Some things we expect: + // + // The Scenic elevations of the PhysicalShapeLayers are monotically + // increasing, even though the elevations we gave them when creating them are + // decreasing. The two should not have any correlation; we're merely mirror + // the paint order using Scenic elevation. + // + // PhysicalShapeLayers created before/below a ChildView do not get their own + // node; PhysicalShapeLayers created afterward do. + // + // Nested PhysicalShapeLayers are collapsed. + + std::vector expected; + + // + // Test root. + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2)); + expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root")); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0})); + expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2)); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon)); + + // + // Child #1: PhysicalShapeLayer + // + // Expect no new commands! Should be composited into base layer. + + // + // Child #2: ChildSceneLayer. + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/4)); + auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New(); + expected.push_back(scenic::NewCreateViewHolderCmd( + /*id=*/5, std::move(view_holder_token1), "")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/3)); + expected.push_back(scenic::NewSetLabelCmd(/*id=*/4, "flutter::ViewHolder")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/4)); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/4, 1.f)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {1, 1, -0.1})); + expected.push_back(scenic::NewSetHitTestBehaviorCmd( + /*id=*/3, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); + + // + // Child #3: PhysicalShapeLayer + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/6)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/6)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/7)); + expected.push_back( + scenic::NewSetLabelCmd(/*id=*/6, "flutter::PhysicalShapeLayer")); + expected.push_back(scenic::NewSetTranslationCmd( + /*id=*/6, {0, 0, -kScenicZElevationBetweenLayers})); + expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/7)); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/7, kOneMinusEpsilon)); + expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/6, /*ignored*/ {})); + expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/8)); + expected.push_back(scenic::NewCreateRectangleCmd( + /*id=*/9, /*width=*/10, /*height=*/10)); + expected.push_back(scenic::NewSetShapeCmd(/*id=*/8, /*shape_id=*/9)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/8, {5, 5, 0})); + expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/10)); + expected.push_back(scenic::NewSetMaterialCmd(/*id=*/8, /*material_id=*/10)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/8)); + + expected.push_back(scenic::NewCreateImageCmd(/*id=*/11, 0, 0, {})); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/6)); + expected.push_back(scenic::NewSetColorCmd(/*id=*/10, /*r*/ 255, /*g*/ 255, + /*b*/ 255, /*a*/ 255)); + expected.push_back( + scenic::NewSetTextureCmd(/*material_id=*/10, /*texture_id=*/11)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7)); + + // + // Grandchild (child of #3): PhysicalShapeLayer + // + // Expect no new commands! Should be composited into parent. + + // + // Child #4: ChildSceneLayer + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/12)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/13)); + auto [view_token2, view_holder_token2] = scenic::ViewTokenPair::New(); + expected.push_back(scenic::NewCreateViewHolderCmd( + /*id=*/14, std::move(view_holder_token2), "")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/13, /*child_id=*/12)); + expected.push_back(scenic::NewSetLabelCmd(/*id=*/13, "flutter::ViewHolder")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/12, /*child_id=*/14)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/13)); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/13, 1.f)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/12, {1, 1, -0.1})); + expected.push_back(scenic::NewSetHitTestBehaviorCmd( + /*id=*/12, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); + + // + // Child #5: PhysicalShapeLayer + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/15)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/15)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/16)); + expected.push_back( + scenic::NewSetLabelCmd(/*id=*/15, "flutter::PhysicalShapeLayer")); + expected.push_back(scenic::NewSetTranslationCmd( + /*id=*/15, {0, 0, -2 * kScenicZElevationBetweenLayers})); + expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/16)); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/16, kOneMinusEpsilon)); + expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/15, /*ignored*/ {})); + expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/17)); + expected.push_back(scenic::NewCreateRectangleCmd( + /*id=*/18, /*width=*/10, /*height=*/10)); + expected.push_back(scenic::NewSetShapeCmd(/*id=*/17, /*shape_id=*/18)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/17, {5, 5, 0})); + expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/19)); + expected.push_back(scenic::NewSetMaterialCmd(/*id=*/17, /*material_id=*/19)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/15, /*child_id=*/17)); + + expected.push_back(scenic::NewCreateImageCmd(/*id=*/20, 0, 0, {})); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/15)); + expected.push_back(scenic::NewSetColorCmd(/*id=*/19, /*r*/ 255, /*g*/ 255, + /*b*/ 255, /*a*/ 255)); + expected.push_back( + scenic::NewSetTextureCmd(/*material_id=*/19, /*texture_id=*/20)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/19)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/18)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/17)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/16)); + + test_context->mock_session.SetExpectedCommands(std::move(expected)); + + // Finally, UpdateScene(). The MockSession will check the emitted commands + // against the list above. + root->UpdateScene(*(test_context->scene_update_context)); + + // Run loop until idle, so that the Session receives and processes + // its method calls. + async_loop_run_until_idle( + async_loop_from_dispatcher(async_get_default_dispatcher())); + + // Ensure we saw enough commands. + EXPECT_EQ(72u, test_context->mock_session.num_enqueued_commands()); +} + +// Create a hierarchy with OpacityLayers, PhysicalShapeLayers and +// ChildSceneLayers, and inspect the commands sent to Scenic. +// +// We are interested in verifying that the opacity values of children are +// correct. +// +TEST_F(FuchsiaLayerTest, Opacity) { + auto test_context = InitTest(); + + // Root. + auto root = std::make_shared(); + SkPath path; + path.addRect(SkRect::MakeWH(10.f, 10.f)); + + // OpacityLayer #1 + auto opacity_layer1 = + std::make_shared(127, SkPoint::Make(0, 0)); + root->Add(opacity_layer1); + + // OpacityLayer #2 + auto opacity_layer2 = + std::make_shared(127, SkPoint::Make(0, 0)); + opacity_layer1->Add(opacity_layer2); + + // Child #1: ChildSceneLayer. + const zx_koid_t kChildLayerId1 = GetChildLayerId(); + auto [unused_view_token1, unused_view_holder_token1] = + scenic::ViewTokenPair::New(); + + ViewHolder::Create(kChildLayerId1, test_context->task_runner, + std::move(unused_view_holder_token1), + /*bind_callback=*/[](scenic::ResourceId id) {}); + // Will destroy only when we go out of scope (i.e. end of the test). + AutoDestroyChildLayerId auto_destroy1(kChildLayerId1); + auto child_view1 = std::make_shared( + kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10), + /*hit_testable=*/false); + opacity_layer2->Add(child_view1); + + // Child #2: PhysicalShapeLayer. + auto physical_shape1 = std::make_shared( + /*color=*/SK_ColorCYAN, + /*shadow_color=*/SK_ColorBLACK, + /*elevation*/ 23.f, path, Clip::antiAlias); + opacity_layer2->Add(physical_shape1); + + // Preroll. + root->Preroll(test_context->preroll_context.get(), SkMatrix()); + + // Create another frame to be the "real" root. Required because + // UpdateScene() traversal expects there to already be a top node. + SceneUpdateContext::Frame frame(*(test_context->scene_update_context), + SkRRect::MakeRect(SkRect::MakeWH(100, 100)), + SK_ColorTRANSPARENT, SK_AlphaOPAQUE, + "fuchsia test root"); + + // Submit the list of command we will expect Scenic to see. + // + // We are interested in verifying that the opacity values of children are + // correct. + + std::vector expected; + + // + // Test root. + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2)); + expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root")); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0})); + expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2)); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon)); + + // + // OpacityLayer #1 + // + // Expect no new commands for this. + + // + // OpacityLayer #2 + // + // Expect no new commands for this. + + // + // Child #1: ChildSceneLayer. + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/4)); + auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New(); + expected.push_back(scenic::NewCreateViewHolderCmd( + /*id=*/5, std::move(view_holder_token1), "")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/3)); + expected.push_back(scenic::NewSetLabelCmd(/*id=*/4, "flutter::ViewHolder")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/4)); + + // Check opacity value. Extra rounding required because we pass alpha as + // a uint/SkAlpha to SceneUpdateContext::Frame. + float opacity1 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f); + opacity1 = SkScalarRoundToInt(opacity1 * 255) / 255.f; + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/4, opacity1)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {1, 1, -0.1})); + expected.push_back(scenic::NewSetHitTestBehaviorCmd( + /*id=*/3, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); + + // + // Child #2: PhysicalShapeLayer + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/6)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/6)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/7)); + expected.push_back( + scenic::NewSetLabelCmd(/*id=*/6, "flutter::PhysicalShapeLayer")); + expected.push_back(scenic::NewSetTranslationCmd( + /*id=*/6, {0, 0, -kScenicZElevationBetweenLayers})); + expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/7)); + + // Check opacity value. Extra rounding required because we pass alpha as + // a uint/SkAlpha to SceneUpdateContext::Frame. + float opacity2 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f); + opacity2 = SkScalarRoundToInt(opacity2 * 255) / 255.f; + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/7, opacity2)); + expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/6, /*ignored*/ {})); + expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/8)); + expected.push_back(scenic::NewCreateRectangleCmd( + /*id=*/9, /*width=*/10, /*height=*/10)); + expected.push_back(scenic::NewSetShapeCmd(/*id=*/8, /*shape_id=*/9)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/8, {5, 5, 0})); + expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/10)); + expected.push_back(scenic::NewSetMaterialCmd(/*id=*/8, + /*material_id=*/10)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/6, + /*child_id=*/8)); + + expected.push_back(scenic::NewCreateImageCmd(/*id=*/11, 0, 0, {})); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/6)); + expected.push_back(scenic::NewSetColorCmd(/*id=*/10, /*r*/ 255, + /*g*/ 255, + /*b*/ 255, /*a*/ 63)); + expected.push_back( + scenic::NewSetTextureCmd(/*material_id=*/10, /*texture_id=*/11)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7)); + + test_context->mock_session.SetExpectedCommands(std::move(expected)); + + // Finally, UpdateScene(). The MockSession will check the emitted + // commands against the list above. + root->UpdateScene(*(test_context->scene_update_context)); + + // Run loop until idle, so that the Session receives and processes + // its method calls. + async_loop_run_until_idle( + async_loop_from_dispatcher(async_get_default_dispatcher())); + + // Ensure we saw enough commands. + EXPECT_EQ(39u, test_context->mock_session.num_enqueued_commands()); +} + +} // namespace testing +} // namespace flutter diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 50e2b0f7e68cf..dfd022a350a0f 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -66,6 +66,11 @@ struct PrerollContext { float total_elevation = 0.0f; bool has_platform_view = false; bool is_opaque = true; +#if defined(OS_FUCHSIA) + // True if, during the traversal so far, we have seen a child_scene_layer. + // Informs whether a layer needs to be system composited. + bool child_scene_layer_exists_below = false; +#endif // defined(OS_FUCHSIA) }; // Represents a single composited layer. Created on the UI thread but then diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 37c28e861bf8a..61560abbed5d4 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -83,7 +83,7 @@ void LayerTree::UpdateScene(SceneUpdateContext& context, context, SkRRect::MakeRect( SkRect::MakeWH(frame_size_.width(), frame_size_.height())), - SK_ColorTRANSPARENT, SK_AlphaOPAQUE); + SK_ColorTRANSPARENT, SK_AlphaOPAQUE, "flutter::LayerTree"); if (root_layer_->needs_system_composite()) { root_layer_->UpdateScene(context); } diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index e6ca90a66a42f..7899c31784b89 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -9,11 +9,6 @@ namespace flutter { -// The OpacityLayer has no real "elevation", but we want to avoid Z-fighting -// when using the system compositor. Choose a small but non-zero value for -// this. -constexpr float kOpacityElevationWhenUsingSystemCompositor = 0.01f; - OpacityLayer::OpacityLayer(SkAlpha alpha, const SkPoint& offset) : alpha_(alpha), offset_(offset) { // Ensure OpacityLayer has only one direct child. @@ -40,8 +35,6 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix child_matrix = matrix; child_matrix.postTranslate(offset_.fX, offset_.fY); - total_elevation_ = context->total_elevation; - context->total_elevation += kOpacityElevationWhenUsingSystemCompositor; context->is_opaque = parent_is_opaque && (alpha_ == SK_AlphaOPAQUE); context->mutators_stack.PushTransform( SkMatrix::MakeTrans(offset_.fX, offset_.fY)); @@ -52,17 +45,7 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->mutators_stack.Pop(); context->mutators_stack.Pop(); context->is_opaque = parent_is_opaque; - context->total_elevation = total_elevation_; -#if defined(OS_FUCHSIA) - if (needs_system_composite()) { - // When using the system compositor, do not include the offset since we - // are rendering as a separate piece of geometry and the offset will be - // baked into that geometry's transform. - frameRRect_ = SkRRect::MakeRect(paint_bounds()); - set_paint_bounds(SkRect::MakeEmpty()); - } else -#endif { set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); if (!context->has_platform_view && context->raster_cache && @@ -123,36 +106,10 @@ void OpacityLayer::Paint(PaintContext& context) const { #if defined(OS_FUCHSIA) void OpacityLayer::UpdateScene(SceneUpdateContext& context) { - FML_DCHECK(needs_system_composite()); - TRACE_EVENT0("flutter", "OpacityLayer::UpdateScene"); - - ContainerLayer* container = GetChildContainer(); - FML_DCHECK(!container->layers().empty()); // OpacityLayer can't be a leaf. - - SceneUpdateContext::Transform transform( - context, SkMatrix::MakeTrans(offset_.fX, offset_.fY)); - - // Retained rendering: speedup by reusing a retained entity node if possible. - // When an entity node is reused, no paint layer is added to the frame so we - // won't call PhysicalShapeLayer::Paint. - LayerRasterCacheKey key(unique_id(), context.Matrix()); - if (context.HasRetainedNode(key)) { - TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); - const scenic::EntityNode& retained_node = context.GetRetainedNode(key); - FML_DCHECK(context.top_entity()); - FML_DCHECK(retained_node.session() == context.session()); - context.top_entity()->embedder_node().AddChild(retained_node); - return; - } - - TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); - // If we can't find an existing retained surface, create one. - SceneUpdateContext::Frame frame( - context, frameRRect_, SK_ColorTRANSPARENT, alpha_, - kOpacityElevationWhenUsingSystemCompositor, total_elevation_, this); - frame.AddPaintLayer(container); - - UpdateSceneChildren(context); + float saved_alpha = context.alphaf(); + context.set_alphaf(context.alphaf() * (alpha_ / 255.f)); + ContainerLayer::UpdateScene(context); + context.set_alphaf(saved_alpha); } #endif // defined(OS_FUCHSIA) diff --git a/flow/layers/opacity_layer.h b/flow/layers/opacity_layer.h index efc5dd4f83e04..658dd1a7b8488 100644 --- a/flow/layers/opacity_layer.h +++ b/flow/layers/opacity_layer.h @@ -43,7 +43,6 @@ class OpacityLayer : public ContainerLayer { SkAlpha alpha_; SkPoint offset_; SkRRect frameRRect_; - float total_elevation_ = 0.0f; FML_DISALLOW_COPY_AND_ASSIGN(OpacityLayer); }; diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index c64049222a8e7..15bad4f5e1f6b 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -52,23 +52,31 @@ void PhysicalShapeLayer::Preroll(PrerollContext* context, context->total_elevation += elevation_; total_elevation_ = context->total_elevation; +#if defined(OS_FUCHSIA) + child_layer_exists_below_ = context->child_scene_layer_exists_below; + context->child_scene_layer_exists_below = false; +#endif + SkRect child_paint_bounds; PrerollChildren(context, matrix, &child_paint_bounds); + +#if defined(OS_FUCHSIA) + if (child_layer_exists_below_) { + set_needs_system_composite(true); + } + context->child_scene_layer_exists_below = + context->child_scene_layer_exists_below || child_layer_exists_below_; +#endif context->total_elevation -= elevation_; if (elevation_ == 0) { set_paint_bounds(path_.getBounds()); } else { -#if defined(OS_FUCHSIA) - // Let the system compositor draw all shadows for us. - set_needs_system_composite(true); -#else // We will draw the shadow in Paint(), so add some margin to the paint // bounds to leave space for the shadow. We fill this whole region and clip // children to it so we don't need to join the child paint bounds. set_paint_bounds(ComputeShadowBounds(path_.getBounds(), elevation_, context->frame_device_pixel_ratio)); -#endif // defined(OS_FUCHSIA) } } @@ -78,30 +86,52 @@ void PhysicalShapeLayer::UpdateScene(SceneUpdateContext& context) { FML_DCHECK(needs_system_composite()); TRACE_EVENT0("flutter", "PhysicalShapeLayer::UpdateScene"); - // Retained rendering: speedup by reusing a retained entity node if possible. - // When an entity node is reused, no paint layer is added to the frame so we - // won't call PhysicalShapeLayer::Paint. - LayerRasterCacheKey key(unique_id(), context.Matrix()); - if (context.HasRetainedNode(key)) { - TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); - const scenic::EntityNode& retained_node = context.GetRetainedNode(key); - FML_DCHECK(context.top_entity()); - FML_DCHECK(retained_node.session() == context.session()); - context.top_entity()->entity_node().AddChild(retained_node); - return; - } + // If there is embedded Fuchsia content in the scene (a ChildSceneLayer), + // PhysicalShapeLayers that appear above the embedded content will be turned + // into their own Scenic layers. + if (child_layer_exists_below_) { + float global_scenic_elevation = + context.GetGlobalElevationForNextScenicLayer(); + float local_scenic_elevation = + global_scenic_elevation - context.scenic_elevation(); + float z_translation = -local_scenic_elevation; + + // Retained rendering: speedup by reusing a retained entity node if + // possible. When an entity node is reused, no paint layer is added to the + // frame so we won't call PhysicalShapeLayer::Paint. + LayerRasterCacheKey key(unique_id(), context.Matrix()); + if (context.HasRetainedNode(key)) { + TRACE_EVENT_INSTANT0("flutter", "retained layer cache hit"); + scenic::EntityNode* retained_node = context.GetRetainedNode(key); + FML_DCHECK(context.top_entity()); + FML_DCHECK(retained_node->session() == context.session()); + + // Re-adjust the elevation. + retained_node->SetTranslation(0.f, 0.f, z_translation); - TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); - // If we can't find an existing retained surface, create one. - SceneUpdateContext::Frame frame(context, frameRRect_, color_, SK_AlphaOPAQUE, - elevation_, total_elevation_, this); - for (auto& layer : layers()) { - if (layer->needs_painting()) { - frame.AddPaintLayer(layer.get()); + context.top_entity()->entity_node().AddChild(*retained_node); + return; } - } - UpdateSceneChildren(context); + TRACE_EVENT_INSTANT0("flutter", "cache miss, creating"); + // If we can't find an existing retained surface, create one. + SceneUpdateContext::Frame frame(context, frameRRect_, SK_ColorTRANSPARENT, + SkScalarRoundToInt(context.alphaf() * 255), + "flutter::PhysicalShapeLayer", + z_translation, this); + + frame.AddPaintLayer(this); + + // Node: UpdateSceneChildren needs to be called here so that |frame| is + // still in scope (and therefore alive) while UpdateSceneChildren is being + // called. + float scenic_elevation = context.scenic_elevation(); + context.set_scenic_elevation(scenic_elevation + local_scenic_elevation); + ContainerLayer::UpdateSceneChildren(context); + context.set_scenic_elevation(scenic_elevation); + } else { + ContainerLayer::UpdateSceneChildren(context); + } } #endif // defined(OS_FUCHSIA) @@ -110,7 +140,18 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Paint"); FML_DCHECK(needs_painting()); +#if defined(OS_FUCHSIA) + // TODO(mikejurka,dworsham,liyl): Re-enable shadow drawing here. + // Shadows are not rendered for PhysicalShapeLayers that exist as separate + // system services; this is to maintain compatibility with the previous + // implementation and has the added benefit of requiring smaller textures, + // since extra space is not needed for the shadows. This behavior might change + // after clients adjust their usage of PhysicalShaperLayer to make elevation + // correlate to desired shadow size. + if (false && !child_layer_exists_below_ && elevation_ != 0) { +#else if (elevation_ != 0) { +#endif DrawShadow(context.leaf_nodes_canvas, path_, shadow_color_, elevation_, SkColorGetA(color_) != 0xff, context.frame_device_pixel_ratio); } diff --git a/flow/layers/physical_shape_layer.h b/flow/layers/physical_shape_layer.h index 40ff19cfac348..106327f47ec9c 100644 --- a/flow/layers/physical_shape_layer.h +++ b/flow/layers/physical_shape_layer.h @@ -42,6 +42,9 @@ class PhysicalShapeLayer : public ContainerLayer { float total_elevation() const { return total_elevation_; } private: +#if defined(OS_FUCHSIA) + bool child_layer_exists_below_ = false; +#endif SkColor color_; SkColor shadow_color_; float elevation_ = 0.0f; diff --git a/flow/layers/physical_shape_layer_unittests.cc b/flow/layers/physical_shape_layer_unittests.cc index d13116c9c44a0..df247e394f1f9 100644 --- a/flow/layers/physical_shape_layer_unittests.cc +++ b/flow/layers/physical_shape_layer_unittests.cc @@ -126,17 +126,11 @@ TEST_F(PhysicalShapeLayerTest, ElevationSimple) { layer->Preroll(preroll_context(), SkMatrix()); // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not do any painting there. -#if defined(OS_FUCHSIA) - EXPECT_EQ(layer->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layer->needs_painting()); - EXPECT_TRUE(layer->needs_system_composite()); -#else EXPECT_EQ(layer->paint_bounds(), PhysicalShapeLayer::ComputeShadowBounds(layer_path.getBounds(), initial_elevation, 1.0f)); EXPECT_TRUE(layer->needs_painting()); EXPECT_FALSE(layer->needs_system_composite()); -#endif EXPECT_EQ(layer->total_elevation(), initial_elevation); // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and @@ -187,18 +181,12 @@ TEST_F(PhysicalShapeLayerTest, ElevationComplex) { // On Fuchsia, the system compositor handles all elevated // PhysicalShapeLayers and their shadows , so we do not do any painting // there. -#if defined(OS_FUCHSIA) - EXPECT_EQ(layers[i]->paint_bounds(), kEmptyRect); - EXPECT_FALSE(layers[i]->needs_painting()); - EXPECT_TRUE(layers[i]->needs_system_composite()); -#else EXPECT_EQ(layers[i]->paint_bounds(), (PhysicalShapeLayer::ComputeShadowBounds( layer_path.getBounds(), initial_elevations[i], 1.0f /* pixel_ratio */))); EXPECT_TRUE(layers[i]->needs_painting()); EXPECT_FALSE(layers[i]->needs_system_composite()); -#endif EXPECT_EQ(layers[i]->total_elevation(), total_elevations[i]); } diff --git a/flow/layers/transform_layer.cc b/flow/layers/transform_layer.cc index 5c7bc44073236..dea9b8f18ebd3 100644 --- a/flow/layers/transform_layer.cc +++ b/flow/layers/transform_layer.cc @@ -56,8 +56,12 @@ void TransformLayer::UpdateScene(SceneUpdateContext& context) { TRACE_EVENT0("flutter", "TransformLayer::UpdateScene"); FML_DCHECK(needs_system_composite()); - SceneUpdateContext::Transform transform(context, transform_); - UpdateSceneChildren(context); + if (!transform_.isIdentity()) { + SceneUpdateContext::Transform transform(context, transform_); + UpdateSceneChildren(context); + } else { + UpdateSceneChildren(context); + } } #endif // defined(OS_FUCHSIA) diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 59524bc9c6e56..407751406bfd6 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -72,14 +72,9 @@ void SceneUpdateContext::CreateFrame(scenic::EntityNode entity_node, // and possibly for its texture. // TODO(SCN-137): Need to be able to express the radii as vectors. scenic::ShapeNode shape_node(session()); - scenic::RoundedRectangle shape( - session_, // session - rrect.width(), // width - rrect.height(), // height - rrect.radii(SkRRect::kUpperLeft_Corner).x(), // top_left_radius - rrect.radii(SkRRect::kUpperRight_Corner).x(), // top_right_radius - rrect.radii(SkRRect::kLowerRight_Corner).x(), // bottom_right_radius - rrect.radii(SkRRect::kLowerLeft_Corner).x() // bottom_left_radius + scenic::Rectangle shape(session_, // session + rrect.width(), // width + rrect.height() // height ); shape_node.SetShape(shape); shape_node.SetTranslation(shape_bounds.width() * 0.5f + shape_bounds.left(), @@ -222,6 +217,9 @@ SceneUpdateContext::ExecutePaintTasks(CompositorContext::ScopedFrame& frame) { surfaces_to_submit.emplace_back(std::move(task.surface)); } paint_tasks_.clear(); + alpha_ = 1.f; + topmost_global_scenic_elevation_ = kScenicZElevationBetweenLayers; + scenic_elevation_ = 0.f; return surfaces_to_submit; } @@ -244,19 +242,22 @@ SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, : Entity(context), previous_scale_x_(context.top_scale_x_), previous_scale_y_(context.top_scale_y_) { + entity_node().SetLabel("flutter::Transform"); if (!transform.isIdentity()) { // TODO(SCN-192): The perspective and shear components in the matrix // are not handled correctly. MatrixDecomposition decomposition(transform); if (decomposition.IsValid()) { + // Don't allow clients to control the z dimension; we control that + // instead to make sure layers appear in proper order. entity_node().SetTranslation(decomposition.translation().x(), // decomposition.translation().y(), // - -decomposition.translation().z() // + 0.f // ); entity_node().SetScale(decomposition.scale().x(), // decomposition.scale().y(), // - decomposition.scale().z() // + 0.f // ); context.top_scale_x_ *= decomposition.scale().x(); context.top_scale_y_ *= decomposition.scale().y(); @@ -277,6 +278,7 @@ SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, : Entity(context), previous_scale_x_(context.top_scale_x_), previous_scale_y_(context.top_scale_y_) { + entity_node().SetLabel("flutter::Transform"); if (scale_x != 1.f || scale_y != 1.f || scale_z != 1.f) { entity_node().SetScale(scale_x, scale_y, scale_z); context.top_scale_x_ *= scale_x; @@ -293,8 +295,8 @@ SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, const SkRRect& rrect, SkColor color, SkAlpha opacity, - float local_elevation, - float world_elevation, + std::string label, + float z_translation, Layer* layer) : Entity(context), rrect_(rrect), @@ -303,23 +305,14 @@ SceneUpdateContext::Frame::Frame(SceneUpdateContext& context, opacity_node_(context.session()), paint_bounds_(SkRect::MakeEmpty()), layer_(layer) { - const float depth = context.frame_physical_depth(); - if (depth > -1 && world_elevation > depth) { - // TODO(mklim): Deal with bounds overflow more elegantly. We'd like to be - // able to have developers specify the behavior here to alternatives besides - // clamping, like normalization on some arbitrary curve. - - // Clamp the local z coordinate at our max bound. Take into account the - // parent z position here to fix clamping in cases where the child is - // overflowing because of its parents. - const float parent_elevation = world_elevation - local_elevation; - local_elevation = depth - parent_elevation; - } - if (local_elevation != 0.0) { - entity_node().SetTranslation(0.f, 0.f, -local_elevation); - } + entity_node().SetLabel(label); + entity_node().SetTranslation(0.f, 0.f, z_translation); entity_node().AddChild(opacity_node_); - opacity_node_.SetOpacity(opacity_ / 255.0f); + // Scenic currently lacks an API to enable rendering of alpha channel; alpha + // channels are only rendered if there is a OpacityNode higher in the tree + // with opacity != 1. For now, clamp to a infinitesimally smaller value than + // 1, which does not cause visual problems in practice. + opacity_node_.SetOpacity(std::min(kOneMinusEpsilon, opacity_ / 255.0f)); } SceneUpdateContext::Frame::~Frame() { @@ -348,6 +341,7 @@ void SceneUpdateContext::Frame::AddPaintLayer(Layer* layer) { SceneUpdateContext::Clip::Clip(SceneUpdateContext& context, const SkRect& shape_bounds) : Entity(context) { + entity_node().SetLabel("flutter::Clip"); SetEntityNodeClipPlanes(entity_node(), shape_bounds); } diff --git a/flow/scene_update_context.h b/flow/scene_update_context.h index 77b0925cc6503..a4d1156cc6021 100644 --- a/flow/scene_update_context.h +++ b/flow/scene_update_context.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_FLOW_SCENE_UPDATE_CONTEXT_H_ #define FLUTTER_FLOW_SCENE_UPDATE_CONTEXT_H_ +#include #include #include #include @@ -22,6 +23,15 @@ namespace flutter { class Layer; +// Scenic currently lacks an API to enable rendering of alpha channel; this only +// happens if there is a OpacityNode higher in the tree with opacity != 1. For +// now, clamp to a infinitesimally smaller value than 1, which does not cause +// visual problems in practice. +constexpr float kOneMinusEpsilon = 1 - FLT_EPSILON; + +// How much layers are separated in Scenic z elevation. +constexpr float kScenicZElevationBetweenLayers = 10.f; + class SceneUpdateContext { public: class SurfaceProducerSurface { @@ -59,7 +69,7 @@ class SceneUpdateContext { // Query a retained entity node (owned by a retained surface) for retained // rendering. virtual bool HasRetainedNode(const LayerRasterCacheKey& key) const = 0; - virtual const scenic::EntityNode& GetRetainedNode( + virtual scenic::EntityNode* GetRetainedNode( const LayerRasterCacheKey& key) = 0; virtual void SubmitSurface( @@ -105,8 +115,8 @@ class SceneUpdateContext { const SkRRect& rrect, SkColor color, SkAlpha opacity, - float local_elevation = 0.0f, - float parent_elevation = 0.0f, + std::string label, + float z_translation = 0.0f, Layer* layer = nullptr); virtual ~Frame(); @@ -175,10 +185,25 @@ class SceneUpdateContext { bool HasRetainedNode(const LayerRasterCacheKey& key) const { return surface_producer_->HasRetainedNode(key); } - const scenic::EntityNode& GetRetainedNode(const LayerRasterCacheKey& key) { + scenic::EntityNode* GetRetainedNode(const LayerRasterCacheKey& key) { return surface_producer_->GetRetainedNode(key); } + // The cumulative alpha value based on all the parent OpacityLayers. + void set_alphaf(float alpha) { alpha_ = alpha; } + float alphaf() { return alpha_; } + + // The global scenic elevation at a given point in the traversal. + float scenic_elevation() { return scenic_elevation_; } + + void set_scenic_elevation(float elevation) { scenic_elevation_ = elevation; } + + float GetGlobalElevationForNextScenicLayer() { + float elevation = topmost_global_scenic_elevation_; + topmost_global_scenic_elevation_ += kScenicZElevationBetweenLayers; + return elevation; + } + private: struct PaintTask { std::unique_ptr surface; @@ -236,6 +261,10 @@ class SceneUpdateContext { float frame_device_pixel_ratio_ = 1.0f; // Ratio between logical and physical pixels. + float alpha_ = 1.0f; + float scenic_elevation_ = 0.f; + float topmost_global_scenic_elevation_ = kScenicZElevationBetweenLayers; + std::vector paint_tasks_; FML_DISALLOW_COPY_AND_ASSIGN(SceneUpdateContext); diff --git a/flow/view_holder.cc b/flow/view_holder.cc index 7f8929d933705..66ce3672a8431 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -102,13 +102,17 @@ ViewHolder::ViewHolder(fml::RefPtr ui_task_runner, void ViewHolder::UpdateScene(SceneUpdateContext& context, const SkPoint& offset, const SkSize& size, + SkAlpha opacity, bool hit_testable) { if (pending_view_holder_token_.value) { entity_node_ = std::make_unique(context.session()); + opacity_node_ = + std::make_unique(context.session()); view_holder_ = std::make_unique( context.session(), std::move(pending_view_holder_token_), "Flutter SceneHost"); - + opacity_node_->AddChild(*entity_node_); + opacity_node_->SetLabel("flutter::ViewHolder"); entity_node_->Attach(*view_holder_); ui_task_runner_->PostTask( [bind_callback = std::move(pending_bind_callback_), @@ -117,9 +121,11 @@ void ViewHolder::UpdateScene(SceneUpdateContext& context, }); } FML_DCHECK(entity_node_); + FML_DCHECK(opacity_node_); FML_DCHECK(view_holder_); - context.top_entity()->embedder_node().AddChild(*entity_node_); + context.top_entity()->embedder_node().AddChild(*opacity_node_); + opacity_node_->SetOpacity(opacity / 255.0f); entity_node_->SetTranslation(offset.x(), offset.y(), -0.1f); entity_node_->SetHitTestBehavior( hit_testable ? fuchsia::ui::gfx::HitTestBehavior::kDefault diff --git a/flow/view_holder.h b/flow/view_holder.h index 82d43eba826d4..10677b8157680 100644 --- a/flow/view_holder.h +++ b/flow/view_holder.h @@ -57,12 +57,14 @@ class ViewHolder { void UpdateScene(SceneUpdateContext& context, const SkPoint& offset, const SkSize& size, + SkAlpha opacity, bool hit_testable); private: fml::RefPtr ui_task_runner_; std::unique_ptr entity_node_; + std::unique_ptr opacity_node_; std::unique_ptr view_holder_; fuchsia::ui::views::ViewHolderToken pending_view_holder_token_; diff --git a/shell/platform/fuchsia/flutter/vulkan_surface.h b/shell/platform/fuchsia/flutter/vulkan_surface.h index 4c67c9d5538c2..2315e0bcee70e 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface.h @@ -133,10 +133,11 @@ class VulkanSurface final // For better safety in retained rendering, Flutter uses a retained // |EntityNode| associated with the retained surface instead of using the // retained surface directly. Hence Flutter can't modify the surface during - // retained rendering. - const scenic::EntityNode& GetRetainedNode() { + // retained rendering. However, the node itself is modifiable to be able + // to adjust its position. + scenic::EntityNode* GetRetainedNode() { used_in_retained_rendering_ = true; - return *retained_node_; + return retained_node_.get(); } // Check whether the retained surface (and its associated |EntityNode|) is diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_pool.h b/shell/platform/fuchsia/flutter/vulkan_surface_pool.h index 00b40437de612..23f8551356265 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_pool.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface_pool.h @@ -43,8 +43,7 @@ class VulkanSurfacePool final { return retained_surfaces_.find(key) != retained_surfaces_.end(); } // For |VulkanSurfaceProducer::GetRetainedNode|. - const scenic::EntityNode& GetRetainedNode( - const flutter::LayerRasterCacheKey& key) { + scenic::EntityNode* GetRetainedNode(const flutter::LayerRasterCacheKey& key) { FML_DCHECK(HasRetainedNode(key)); return retained_surfaces_[key].vk_surface->GetRetainedNode(); } diff --git a/shell/platform/fuchsia/flutter/vulkan_surface_producer.h b/shell/platform/fuchsia/flutter/vulkan_surface_producer.h index 77457bac32cdd..5ea1415cf537f 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface_producer.h +++ b/shell/platform/fuchsia/flutter/vulkan_surface_producer.h @@ -49,7 +49,7 @@ class VulkanSurfaceProducer final } // |flutter::SceneUpdateContext::GetRetainedNode| - const scenic::EntityNode& GetRetainedNode( + scenic::EntityNode* GetRetainedNode( const flutter::LayerRasterCacheKey& key) override { return surface_pool_->GetRetainedNode(key); } From 1eea062c99ec8411f9e67046194b1f0bdfb0eb44 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 20:56:02 -0400 Subject: [PATCH 107/386] Roll src/third_party/skia 3c358420df4a..d1988219065a (3 commits) (#17279) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a37c5e7a64dd5..e59dfbda43411 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '3c358420df4acd0e4efb57fffc7c1a28e0488da9', + 'skia_revision': 'd1988219065ad7cf6e56f8c72682372ff41fe3ba', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 79349ecfab509..bd993c34c72f5 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 5ae073fe9ba4767aae57b5f1ca3988b7 +Signature: fab57c5339109e1e97cd8d611dd69072 UNUSED LICENSES: From 518c9e09cc796715d96d80db78edc3eb86f497ae Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 23 Mar 2020 18:31:02 -0700 Subject: [PATCH 108/386] Remove checks for the always true using_fuchsia_sdk flag in all GN files. (#17261) --- BUILD.gn | 6 ++--- common/config.gni | 11 ++++------ common/fuchsia_config.gni | 2 +- flow/BUILD.gn | 19 +++++----------- fml/BUILD.gn | 29 ++++++++----------------- lib/snapshot/BUILD.gn | 29 +++++-------------------- lib/ui/BUILD.gn | 14 +++++------- shell/common/BUILD.gn | 5 +---- shell/platform/BUILD.gn | 8 +++---- shell/platform/fuchsia/BUILD.gn | 2 +- shell/platform/fuchsia/flutter/BUILD.gn | 17 +++------------ shell/testing/BUILD.gn | 6 +---- sky/packages/sky_engine/BUILD.gn | 20 ----------------- vulkan/BUILD.gn | 16 +++++--------- 14 files changed, 48 insertions(+), 136 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index c62b1e9610067..d35c608ba1d63 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -19,7 +19,7 @@ group("flutter") { public_deps = [] - if (!(is_fuchsia && using_fuchsia_sdk)) { + if (!is_fuchsia) { public_deps += [ "//flutter/lib/snapshot:generate_snapshot_bin", "//flutter/lib/snapshot:kernel_platform_files", @@ -40,14 +40,14 @@ group("flutter") { public_deps += [ "//flutter/tools/const_finder" ] } - if (is_fuchsia && using_fuchsia_sdk) { + if (is_fuchsia) { public_deps += [ "//flutter/shell/platform/fuchsia", "//flutter/shell/testing($host_toolchain)", ] } - if (!is_fuchsia && !is_fuchsia_host) { + if (!is_fuchsia) { if (current_toolchain == host_toolchain) { public_deps += [ "//flutter/flutter_frontend_server:frontend_server", diff --git a/common/config.gni b/common/config.gni index 1b7d18f670ccd..a3eb20b19a5ae 100644 --- a/common/config.gni +++ b/common/config.gni @@ -12,12 +12,8 @@ if (target_cpu == "arm" || target_cpu == "arm64") { import("//build/config/arm.gni") } -if (is_ios || is_mac) { - if (using_fuchsia_sdk) { - import("//build/toolchain/clang.gni") - } else { - import("//build/toolchain/clang_toolchain.gni") - } +if (is_fuchsia) { + import("//build/toolchain/clang.gni") } declare_args() { @@ -64,7 +60,8 @@ if (flutter_runtime_mode == "debug") { feature_defines_list += [ "FLUTTER_RUNTIME_MODE=0" ] } -if (is_fuchsia && using_fuchsia_sdk) { +if (is_fuchsia) { + # TODO(chinmaygarde): This is always set. The macro may now be removed. feature_defines_list += [ "FUCHSIA_SDK=1" ] } diff --git a/common/fuchsia_config.gni b/common/fuchsia_config.gni index c8c55b59c5cf2..96e120fba29d7 100644 --- a/common/fuchsia_config.gni +++ b/common/fuchsia_config.gni @@ -4,7 +4,7 @@ import("config.gni") -if (is_fuchsia && using_fuchsia_sdk) { +if (is_fuchsia) { dart_tools_root = "//flutter/tools/fuchsia/dart" fuchsia_base = "//flutter/shell/platform/fuchsia" flutter_runner_base = "$fuchsia_base/flutter/" diff --git a/flow/BUILD.gn b/flow/BUILD.gn index e25b5dc8957a3..c3fea8568e9ea 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn @@ -86,20 +86,11 @@ source_set("flow") { "view_holder.h", ] - if (using_fuchsia_sdk) { - public_deps += [ - "$fuchsia_sdk_root/fidl:fuchsia.ui.app", - "$fuchsia_sdk_root/fidl:fuchsia.ui.gfx", - "$fuchsia_sdk_root/pkg:scenic_cpp", - ] - } else { - public_deps += [ - "//garnet/public/lib/ui/scenic/cpp", - "//sdk/fidl/fuchsia.ui.scenic", - "//topaz/public/dart-pkg/zircon", - "//zircon/public/lib/zx", - ] - } + public_deps += [ + "$fuchsia_sdk_root/fidl:fuchsia.ui.app", + "$fuchsia_sdk_root/fidl:fuchsia.ui.gfx", + "$fuchsia_sdk_root/pkg:scenic_cpp", + ] } } diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 08d60aaad7b9f..12d30ee83f8d8 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -180,25 +180,14 @@ source_set("fml") { "platform/fuchsia/paths_fuchsia.cc", ] - if (using_fuchsia_sdk) { - public_deps += [ - "$fuchsia_sdk_root/pkg:async-cpp", - "$fuchsia_sdk_root/pkg:async-loop-cpp", - "$fuchsia_sdk_root/pkg:async-loop-default", - "$fuchsia_sdk_root/pkg:trace", - "$fuchsia_sdk_root/pkg:trace-engine", - "$fuchsia_sdk_root/pkg:zx", - ] - } else { - public_deps += [ - "//zircon/public/lib/async-cpp", - "//zircon/public/lib/async-loop-cpp", - "//zircon/public/lib/async-loop-default", - "//zircon/public/lib/trace", - "//zircon/public/lib/trace-engine", - "//zircon/public/lib/zx", - ] - } + public_deps += [ + "$fuchsia_sdk_root/pkg:async-cpp", + "$fuchsia_sdk_root/pkg:async-loop-cpp", + "$fuchsia_sdk_root/pkg:async-loop-default", + "$fuchsia_sdk_root/pkg:trace", + "$fuchsia_sdk_root/pkg:trace-engine", + "$fuchsia_sdk_root/pkg:zx", + ] } if (is_win) { @@ -278,7 +267,7 @@ executable("fml_unittests") { "//flutter/testing", ] - if (is_fuchsia && using_fuchsia_sdk) { + if (is_fuchsia) { libs = [ "${fuchsia_sdk_path}/arch/${target_cpu}/sysroot/lib/libzircon.so" ] } } diff --git a/lib/snapshot/BUILD.gn b/lib/snapshot/BUILD.gn index 41740be8f391a..f21f3dffdd2c6 100644 --- a/lib/snapshot/BUILD.gn +++ b/lib/snapshot/BUILD.gn @@ -30,12 +30,7 @@ compiled_action("generate_snapshot_bin") { tool = "//third_party/dart/runtime/bin:gen_snapshot" } - if ((is_fuchsia || is_fuchsia_host) && !using_fuchsia_sdk) { - platform_kernel = - "$root_out_dir/flutter_runner_patched_sdk/platform_strong.dill" - } else { - platform_kernel = "$root_out_dir/flutter_patched_sdk/platform_strong.dill" - } + platform_kernel = "$root_out_dir/flutter_patched_sdk/platform_strong.dill" inputs = [ platform_kernel, @@ -217,11 +212,7 @@ bin_to_linkable("platform_strong_dill_linkable") { deps = [ ":kernel_platform_files", ] - if ((is_fuchsia || is_fuchsia_host) && !using_fuchsia_sdk) { - input = "$root_out_dir/flutter_runner_patched_sdk/platform_strong.dill" - } else { - input = "$root_out_dir/flutter_patched_sdk/platform_strong.dill" - } + input = "$root_out_dir/flutter_patched_sdk/platform_strong.dill" symbol = "kPlatformStrongDill" size_symbol = "kPlatformStrongDillSize" executable = false @@ -283,16 +274,8 @@ compile_platform("strong_platform") { } # Fuchsia's snapshot requires a different platform with extra dart: libraries. -if ((is_fuchsia || is_fuchsia_host) && !using_fuchsia_sdk) { - group("kernel_platform_files") { - public_deps = [ - "//topaz/runtime/flutter_runner/kernel:kernel_platform_files", - ] - } -} else { - group("kernel_platform_files") { - public_deps = [ - ":strong_platform", - ] - } +group("kernel_platform_files") { + public_deps = [ + ":strong_platform", + ] } diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index 4d3210d3bef37..a581708f03c24 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -135,15 +135,11 @@ source_set("ui") { "compositing/scene_host.h", ] - if (using_fuchsia_sdk) { - deps += [ - "$fuchsia_sdk_root/pkg:async-cpp", - "//flutter/shell/platform/fuchsia/dart-pkg/fuchsia", - "//flutter/shell/platform/fuchsia/dart-pkg/zircon", - ] - } else { - deps += [ "//topaz/public/dart-pkg/zircon" ] - } + deps += [ + "$fuchsia_sdk_root/pkg:async-cpp", + "//flutter/shell/platform/fuchsia/dart-pkg/fuchsia", + "//flutter/shell/platform/fuchsia/dart-pkg/zircon", + ] } } diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 8e4a48e2bb000..66684679c3de3 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -12,11 +12,8 @@ if (is_fuchsia) { } config("vulkan_config") { - if (using_fuchsia_sdk) { + if (is_fuchsia) { include_dirs = [ "$fuchsia_sdk_root/vulkan/include" ] - } else if (is_fuchsia) { - include_dirs = - [ "//third_party/vulkan_loader_and_validation_layers/include" ] } else { include_dirs = [ "//third_party/vulkan/src" ] } diff --git a/shell/platform/BUILD.gn b/shell/platform/BUILD.gn index 4b881909d14e2..925177e67472b 100644 --- a/shell/platform/BUILD.gn +++ b/shell/platform/BUILD.gn @@ -27,11 +27,9 @@ group("platform") { "windows", ] } else if (is_fuchsia) { - if (using_fuchsia_sdk) { - deps = [ - "fuchsia", - ] - } + deps = [ + "fuchsia", + ] } else { assert(false, "Unknown/Unsupported platform.") } diff --git a/shell/platform/fuchsia/BUILD.gn b/shell/platform/fuchsia/BUILD.gn index 9afc96bc02d40..050719ec7f623 100644 --- a/shell/platform/fuchsia/BUILD.gn +++ b/shell/platform/fuchsia/BUILD.gn @@ -7,7 +7,7 @@ import("//flutter/common/config.gni") import("//flutter/tools/fuchsia/dart.gni") import("//flutter/tools/fuchsia/fuchsia_host_bundle.gni") -if (using_fuchsia_sdk) { +if (is_fuchsia) { product_suffix = "" is_product = false diff --git a/shell/platform/fuchsia/flutter/BUILD.gn b/shell/platform/fuchsia/flutter/BUILD.gn index 8e09a13f27d6c..bd4da2779d507 100644 --- a/shell/platform/fuchsia/flutter/BUILD.gn +++ b/shell/platform/fuchsia/flutter/BUILD.gn @@ -25,17 +25,6 @@ shell_gpu_configuration("fuchsia_gpu_configuration") { # 1. Kernel snapshot framework mode. # 2. Profiler symbols. -# Dependencies for flutter tooling -# -# While not required to run a flutter mod, these allow interacting -# with flutter via the fx tool and need to be built. -# -# This is only for builds in topaz tree. -flutter_tool_deps = [] -if (!using_fuchsia_sdk) { - flutter_tool_deps += [ "//third_party/dart-pkg/git/flutter/packages/flutter_tools:fuchsia_attach($host_toolchain)" ] -} - flutter_runner("jit") { output_name = "flutter_jit_runner" product = false @@ -50,9 +39,9 @@ flutter_runner("jit") { } extra_deps = [ - "//third_party/dart/runtime:libdart_jit", - "//third_party/dart/runtime/platform:libdart_platform_jit", - ] + flutter_tool_deps + "//third_party/dart/runtime:libdart_jit", + "//third_party/dart/runtime/platform:libdart_platform_jit", + ] } flutter_runner("jit_product") { diff --git a/shell/testing/BUILD.gn b/shell/testing/BUILD.gn index d7b793320b568..fce083fc12b1b 100644 --- a/shell/testing/BUILD.gn +++ b/shell/testing/BUILD.gn @@ -29,10 +29,6 @@ executable("testing") { ] if (is_fuchsia) { - if (!using_fuchsia_sdk) { - deps += [ "//garnet/public/lib/ui/scenic:client" ] - } else { - deps += [ "$fuchsia_sdk_root/pkg:scenic_cpp" ] - } + deps += [ "$fuchsia_sdk_root/pkg:scenic_cpp" ] } } diff --git a/sky/packages/sky_engine/BUILD.gn b/sky/packages/sky_engine/BUILD.gn index d0af03dc96375..9d627c5dbeb5e 100644 --- a/sky/packages/sky_engine/BUILD.gn +++ b/sky/packages/sky_engine/BUILD.gn @@ -229,23 +229,3 @@ dart_pkg("sky_engine") { "$service_isolate_dir/vmservice_server.dart", ] } - -if (is_fuchsia && !using_fuchsia_sdk) { - import("//build/dart/dart_library.gni") - - dart_library("sky_engine_dart") { - package_name = "sky_engine" - - package_root = "$root_gen_dir/dart-pkg/sky_engine" - - sources = [] - - disable_analysis = true - - non_dart_deps = [ - # This will ensure all the source files needed for this package are - # copied to the right location. - ":sky_engine", - ] - } -} diff --git a/vulkan/BUILD.gn b/vulkan/BUILD.gn index 13781b5a66141..c701763ea121f 100644 --- a/vulkan/BUILD.gn +++ b/vulkan/BUILD.gn @@ -5,17 +5,13 @@ import("//build/fuchsia/sdk.gni") config("vulkan_config") { - if (using_fuchsia_sdk) { - include_dirs = [ "$fuchsia_sdk_root/vulkan/include" ] - } else if (is_fuchsia) { - include_dirs = - [ "//third_party/vulkan_loader_and_validation_layers/include" ] - } else { - include_dirs = [ "//third_party/vulkan/src" ] - } - + include_dirs = [] + defines = [] if (is_fuchsia) { - defines = [ "VK_USE_PLATFORM_FUCHSIA=1" ] + include_dirs += [ "$fuchsia_sdk_root/vulkan/include" ] + defines += [ "VK_USE_PLATFORM_FUCHSIA=1" ] + } else { + include_dirs += [ "//third_party/vulkan/src" ] } } From 0015b195446e7a2e8f0c1a9ee2c70b4cb0495356 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 22:01:01 -0400 Subject: [PATCH 109/386] Roll fuchsia/sdk/core/mac-amd64 from WaNBa... to gkIC0... (#17282) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index e59dfbda43411..74b5a0f65576f 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'WaNBaYEWWIMn_T7f0ejsBbfLkEsYWF5hk_JgUjUKkO4C' + 'version': 'gkIC0axhhXCWFmu6PmhcVlRtJDk0Q1F6Gu15c8kx05IC' } ], 'condition': 'host_os == "mac"', From 48d11656279445f271b2bac4cb762eb1121419cf Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 22:26:02 -0400 Subject: [PATCH 110/386] Roll fuchsia/sdk/core/linux-amd64 from h8m0G... to y2Piw... (#17283) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 74b5a0f65576f..54b1d9ae1a739 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'h8m0GOzmMI7qIReFkpBbj1kFWGCbEyf02lBE9zSxTKsC' + 'version': 'y2PiwNfLWibXm32tDyDeW0kxPcRcGlmMgqxWk32e984C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 4a151c2a89c7d..09e74ac82e574 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: fc6e387341f6b181ccee7bd540135291 +Signature: b74c2f7b452bd65c8c7b53636e06ac5a UNUSED LICENSES: From 33895cf6ba516c9fb0f405c96aa1abf2eeceb560 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 23 Mar 2020 23:26:01 -0400 Subject: [PATCH 111/386] Roll src/third_party/skia d1988219065a..c97c90072499 (2 commits) (#17285) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 54b1d9ae1a739..c7d2614433592 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd1988219065ad7cf6e56f8c72682372ff41fe3ba', + 'skia_revision': 'c97c9007249952b2168e0ae3650ddfea36f76b07', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index bd993c34c72f5..652fe44ae84dd 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: fab57c5339109e1e97cd8d611dd69072 +Signature: c11944f8b3438729cd124a85b612f4af UNUSED LICENSES: From 704a869e0aac64bc77348e6acb1a72ec471ce755 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 24 Mar 2020 00:51:02 -0400 Subject: [PATCH 112/386] Roll src/third_party/dart cbd67124d10e..8246621d4da3 (39 commits) (#17286) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index c7d2614433592..dcbb76ccc42c1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'cbd67124d10e277e52e94f37d32aa517afeb549a', + 'dart_revision': '8246621d4da394bd006202bc5b1122b69fd3a4cb', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -60,7 +60,7 @@ vars = { 'dart_http_retry_tag': '0.1.1', 'dart_http_tag': '0.12.0+2', 'dart_http_throttle_tag': '1.0.2', - 'dart_intl_tag': '0.15.7', + 'dart_intl_tag': '0.16.1', 'dart_json_rpc_2_tag': '2.0.9', 'dart_linter_tag': '0.1.113', 'dart_logging_tag': '0.11.3+2', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1ec6ad0a25281..a0a82a81992c1 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 1c27e10f486057edc8e288aa560f6031 +Signature: f552bc1ed946277be9abd4cfcd6b9499 UNUSED LICENSES: From d7ad50c96f058b0286e7e5e2a9d014500a5c5479 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 24 Mar 2020 01:41:02 -0400 Subject: [PATCH 113/386] Roll src/third_party/skia c97c90072499..6cb8168c2b9b (1 commits) (#17287) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index dcbb76ccc42c1..507ba4932017b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'c97c9007249952b2168e0ae3650ddfea36f76b07', + 'skia_revision': '6cb8168c2b9b757110883a6974e45c7f76db228c', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 652fe44ae84dd..bd993c34c72f5 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c11944f8b3438729cd124a85b612f4af +Signature: fab57c5339109e1e97cd8d611dd69072 UNUSED LICENSES: From 38d053f4fd25897ec55ae2fe7774d04806c425d6 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 24 Mar 2020 04:01:01 -0400 Subject: [PATCH 114/386] Roll src/third_party/skia 6cb8168c2b9b..e5ca6ba2c84c (3 commits) (#17288) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 507ba4932017b..3a41aba4230c9 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6cb8168c2b9b757110883a6974e45c7f76db228c', + 'skia_revision': 'e5ca6ba2c84c029ae3ce15a141927d6f621311ae', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index bd993c34c72f5..f159612870328 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: fab57c5339109e1e97cd8d611dd69072 +Signature: f09cc62874b1c25b9fcd3eeae57e277e UNUSED LICENSES: From 3a68f689996ffed5c2b115acc81d0cd08a49a612 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 24 Mar 2020 12:49:13 -0700 Subject: [PATCH 115/386] Document flutter::DartServiceIsolate. (#17301) --- runtime/dart_service_isolate.h | 70 ++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/runtime/dart_service_isolate.h b/runtime/dart_service_isolate.h index 5bd9b5beb3f65..1649d3e3343ed 100644 --- a/runtime/dart_service_isolate.h +++ b/runtime/dart_service_isolate.h @@ -11,16 +11,54 @@ #include #include "flutter/fml/compiler_specific.h" - #include "third_party/dart/runtime/include/dart_api.h" namespace flutter { +//------------------------------------------------------------------------------ +/// @brief Utility methods for interacting with the DartVM managed service +/// isolate present in debug and profile runtime modes. +/// class DartServiceIsolate { public: + //---------------------------------------------------------------------------- + /// The handle used to refer to callbacks registered with the service isolate. + /// + using CallbackHandle = ptrdiff_t; + + //---------------------------------------------------------------------------- + /// A callback made by the Dart VM when the observatory is ready. The argument + /// indicates the observatory URI. + /// using ObservatoryServerStateCallback = - std::function; + std::function; + //---------------------------------------------------------------------------- + /// @brief Start the service isolate. This call may only be made in the + /// Dart VM initiated isolate creation callback. It is only valid + /// to make this call when the VM explicitly requests the creation + /// of the service isolate. The VM does this by specifying the + /// script URI to be `DART_VM_SERVICE_ISOLATE_NAME`. The isolate + /// to be designated as the service isolate must already be + /// created (but not running) when this call is made. + /// + /// @param[in] server_ip The service protocol IP address. + /// @param[in] server_port The service protocol port. + /// @param[in] embedder_tag_handler The library tag handler. + /// @param[in] disable_origin_check If websocket origin checks must + /// be enabled. + /// @param[in] disable_service_auth_codes If service auth codes must be + /// enabled. + /// @param[in] enable_service_port_fallback If fallback to port 0 must be + /// enabled when the bind fails. + /// @param error The error when this method + /// returns false. This string must + /// be freed by the caller using + /// `free`. + /// + /// @return If the startup was successful. Refer to the `error` for + /// details on failure. + /// static bool Startup(std::string server_ip, intptr_t server_port, Dart_LibraryTagHandler embedder_tag_handler, @@ -29,14 +67,32 @@ class DartServiceIsolate { bool enable_service_port_fallback, char** error); - using CallbackHandle = ptrdiff_t; - - // Returns a handle for the callback that can be used in - // RemoveServerStatusCallback + //---------------------------------------------------------------------------- + /// @brief Add a callback that will get invoked when the observatory + /// starts up. If the observatory has already started before this + /// call is made, the callback is invoked immediately. + /// + /// This method is thread safe. + /// + /// @param[in] callback The callback with information about the observatory. + /// + /// @return A handle for the callback that can be used later in + /// `RemoveServerStatusCallback`. + /// [[nodiscard]] static CallbackHandle AddServerStatusCallback( const ObservatoryServerStateCallback& callback); - // Accepts the handle returned by AddServerStatusCallback + //---------------------------------------------------------------------------- + /// @brief Removed a callback previously registered via + /// `AddServiceStatusCallback`. + /// + /// This method is thread safe. + /// + /// @param[in] handle The handle + /// + /// @return If the callback was unregistered. This may fail if there was + /// no such callback with that handle. + /// static bool RemoveServerStatusCallback(CallbackHandle handle); private: From c93b67aa81f9199ec1e1eddfab1a0a2673da789f Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 25 Mar 2020 09:26:02 +1300 Subject: [PATCH 116/386] Add a Linux Shell that uses GTK for rendering. (#16977) --- ci/licenses_golden/licenses_flutter | 5 + shell/platform/linux/BUILD.gn | 56 +++- shell/platform/linux/config.gni | 11 + shell/platform/linux/config/BUILD.gn | 10 + shell/platform/linux/fl_dart_project.cc | 140 +++++++++ shell/platform/linux/fl_view.cc | 287 ++++++++++++++++++ .../public/flutter_linux/fl_dart_project.h | 27 ++ .../linux/public/flutter_linux/fl_view.h | 24 ++ .../public/flutter_linux/flutter_linux.h | 15 + 9 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 shell/platform/linux/config.gni create mode 100644 shell/platform/linux/fl_dart_project.cc create mode 100644 shell/platform/linux/fl_view.cc create mode 100644 shell/platform/linux/public/flutter_linux/fl_dart_project.h create mode 100644 shell/platform/linux/public/flutter_linux/fl_view.h create mode 100644 shell/platform/linux/public/flutter_linux/flutter_linux.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 135e7a141dd5f..ebc6f87e28903 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1145,6 +1145,11 @@ FILE: ../../../flutter/shell/platform/glfw/platform_handler.h FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h +FILE: ../../../flutter/shell/platform/linux/fl_dart_project.cc +FILE: ../../../flutter/shell/platform/linux/fl_view.cc +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h +FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/flutter_linux.h FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.cc FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/dart_project_unittests.cc diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 54e1bbbd7c709..d0221b1e392ca 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -5,15 +5,23 @@ assert(is_linux) import("//flutter/shell/platform/glfw/config.gni") +import("//flutter/shell/platform/linux/config.gni") group("linux") { + deps = [] if (build_glfw_shell) { - deps = [ + deps += [ ":flutter_linux_glfw", "//flutter/shell/platform/glfw:publish_headers_glfw", "//flutter/shell/platform/glfw/client_wrapper:publish_wrapper_glfw", ] } + if (build_linux_shell) { + deps += [ + ":flutter_linux_gtk", + ":publish_headers_linux", + ] + } } # Temporary workaround for the issue describe in @@ -37,3 +45,49 @@ if (build_glfw_shell) { public_configs = [ "//flutter:config" ] } } + +if (build_linux_shell) { + _public_headers = [ + "public/flutter_linux/fl_dart_project.h", + "public/flutter_linux/fl_view.h", + "public/flutter_linux/flutter_linux.h", + ] + + config("relative_flutter_linux_headers") { + include_dirs = [ "public" ] + } + + source_set("flutter_linux") { + sources = [ + "fl_dart_project.cc", + "fl_view.cc", + ] + + configs += [ + "//flutter/shell/platform/linux/config:gtk", + "//flutter/shell/platform/linux/config:egl", + ] + + # Set flag to stop headers being directly included (library users should not do this) + defines = [ "FLUTTER_LINUX_COMPILATION" ] + + deps = [ + "//flutter/shell/platform/embedder:embedder_with_symbol_prefix", + ] + } + + shared_library("flutter_linux_gtk") { + deps = [ + ":flutter_linux", + ] + + public_configs = [ "//flutter:config" ] + } + + copy("publish_headers_linux") { + sources = _public_headers + outputs = [ + "$root_out_dir/flutter_linux/{{source_file_part}}", + ] + } +} diff --git a/shell/platform/linux/config.gni b/shell/platform/linux/config.gni new file mode 100644 index 0000000000000..012f3d5438b44 --- /dev/null +++ b/shell/platform/linux/config.gni @@ -0,0 +1,11 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +declare_args() { + # Whether to build the Linux (GTK) shell for the host platform, if available. + # + # The Linux shell is not currently built by default as the CI system doesn't + # (yet) have GTK as a dependency. When that is ready the flag will be removed. + build_linux_shell = false +} diff --git a/shell/platform/linux/config/BUILD.gn b/shell/platform/linux/config/BUILD.gn index 48cf022355cff..19f8001db2fb5 100644 --- a/shell/platform/linux/config/BUILD.gn +++ b/shell/platform/linux/config/BUILD.gn @@ -3,7 +3,17 @@ # found in the LICENSE file. import("//build/config/linux/pkg_config.gni") +import("//flutter/shell/platform/linux/config.gni") pkg_config("x11") { packages = [ "x11" ] } + +if (build_linux_shell) { + pkg_config("gtk") { + packages = [ "gtk+-3.0" ] + } + pkg_config("egl") { + packages = [ "egl" ] + } +} diff --git a/shell/platform/linux/fl_dart_project.cc b/shell/platform/linux/fl_dart_project.cc new file mode 100644 index 0000000000000..cbabc8cb9648f --- /dev/null +++ b/shell/platform/linux/fl_dart_project.cc @@ -0,0 +1,140 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h" + +#include + +/** + * FlDartProject: + * + * #FlDartProject represents a Dart project. It is used provide information + * about the application when creating a #FlView. + */ + +struct _FlDartProject { + GObject parent_instance; + + gchar* assets_path; + gchar* icu_data_path; +}; + +enum { PROP_ASSETS_PATH = 1, PROP_ICU_DATA_PATH, PROP_LAST }; + +G_DEFINE_TYPE(FlDartProject, fl_dart_project, G_TYPE_OBJECT) + +static void fl_dart_project_set_property(GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) { + FlDartProject* self = FL_DART_PROJECT(object); + + switch (prop_id) { + case PROP_ASSETS_PATH: + g_free(self->assets_path); + self->assets_path = g_strdup(g_value_get_string(value)); + break; + case PROP_ICU_DATA_PATH: + g_free(self->icu_data_path); + self->icu_data_path = g_strdup(g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void fl_dart_project_get_property(GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) { + FlDartProject* self = FL_DART_PROJECT(object); + + switch (prop_id) { + case PROP_ASSETS_PATH: + g_value_set_string(value, self->assets_path); + break; + case PROP_ICU_DATA_PATH: + g_value_set_string(value, self->icu_data_path); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void fl_dart_project_dispose(GObject* object) { + FlDartProject* self = FL_DART_PROJECT(object); + + g_clear_pointer(&self->assets_path, g_free); + g_clear_pointer(&self->icu_data_path, g_free); + + G_OBJECT_CLASS(fl_dart_project_parent_class)->dispose(object); +} + +static void fl_dart_project_class_init(FlDartProjectClass* klass) { + G_OBJECT_CLASS(klass)->set_property = fl_dart_project_set_property; + G_OBJECT_CLASS(klass)->get_property = fl_dart_project_get_property; + G_OBJECT_CLASS(klass)->dispose = fl_dart_project_dispose; + + g_object_class_install_property( + G_OBJECT_CLASS(klass), PROP_ASSETS_PATH, + g_param_spec_string( + "assets-path", "assets-path", "Path to Flutter assets", nullptr, + static_cast(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); + g_object_class_install_property( + G_OBJECT_CLASS(klass), PROP_ICU_DATA_PATH, + g_param_spec_string( + "icu-data-path", "icu-data-path", "Path to ICU data", nullptr, + static_cast(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); +} + +static void fl_dart_project_init(FlDartProject* self) {} + +/** + * fl_dart_project_new: + * @assets_path: a file path, e.g. "build/assets" + * @icu_data_path: a file path, e.g. "build/icudtl.dat" + * + * Create a Flutter project. + * + * Returns: a new #FlDartProject + */ +G_MODULE_EXPORT FlDartProject* fl_dart_project_new(const gchar* assets_path, + const gchar* icu_data_path) { + return static_cast( + g_object_new(fl_dart_project_get_type(), "assets-path", assets_path, + "icu-data-path", icu_data_path, nullptr)); +} + +/** + * fl_dart_project_get_assets_path: + * @view: a #FlDartProject + * + * Get the path to the directory containing the assets used in the Flutter + * application. + * + * Returns: a file path, e.g. "build/assets" + */ +G_MODULE_EXPORT const gchar* fl_dart_project_get_assets_path( + FlDartProject* self) { + g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); + return self->assets_path; +} + +/** + * fl_dart_project_get_icu_data_path: + * @view: a #FlDartProject + * + * Get the path to the ICU data file in the Flutter application. + * + * Returns: a file path, e.g. "build/icudtl.dat" + */ +G_MODULE_EXPORT const gchar* fl_dart_project_get_icu_data_path( + FlDartProject* self) { + g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); + return self->icu_data_path; +} diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc new file mode 100644 index 0000000000000..218f1aed410c5 --- /dev/null +++ b/shell/platform/linux/fl_view.cc @@ -0,0 +1,287 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h" + +#include +#include +#include +#include + +#include "flutter/shell/platform/embedder/embedder.h" + +/** + * FlView: + * + * #FlView is a GTK widget that is capable of displaying a Flutter application. + */ + +struct _FlView { + GtkWidget parent_instance; + + EGLDisplay egl_display; + EGLSurface egl_surface; + EGLContext egl_context; + + FlDartProject* flutter_project; + FLUTTER_API_SYMBOL(FlutterEngine) flutter_engine; +}; + +enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST }; + +G_DEFINE_TYPE(FlView, fl_view, GTK_TYPE_WIDGET) + +static gboolean initialize_egl(FlView* self) { + /* Note that we don't provide the XDisplay from GTK, this would make both + * GTK and EGL share the same X connection and this would crash when used by + * a Flutter thread. So the EGL display and GTK both have separate + * connections. + */ + self->egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + EGLint egl_major, egl_minor; + if (!eglInitialize(self->egl_display, &egl_major, &egl_minor)) { + g_warning("Failed to initialze EGL"); + return FALSE; + } + // TODO(robert-ancell): It would probably be useful to store the EGL version + // for debugging purposes + + EGLint attributes[] = {EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, + EGL_NONE}; + EGLConfig egl_config; + EGLint n_config; + if (!eglChooseConfig(self->egl_display, attributes, &egl_config, 1, + &n_config)) { + g_warning("Failed to choose EGL config"); + return FALSE; + } + if (n_config == 0) { + g_warning("Failed to find appropriate EGL config"); + return FALSE; + } + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + g_warning("Failed to bind EGL OpenGL ES API"); + return FALSE; + } + + Window xid = gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(self))); + self->egl_surface = + eglCreateWindowSurface(self->egl_display, egl_config, xid, nullptr); + EGLint context_attributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; + self->egl_context = eglCreateContext(self->egl_display, egl_config, + EGL_NO_CONTEXT, context_attributes); + EGLint value; + eglQueryContext(self->egl_display, self->egl_context, + EGL_CONTEXT_CLIENT_VERSION, &value); + + return TRUE; +} + +static void* fl_view_gl_proc_resolver(void* user_data, const char* name) { + return reinterpret_cast(eglGetProcAddress(name)); +} + +static bool fl_view_gl_make_current(void* user_data) { + FlView* self = static_cast(user_data); + + if (!eglMakeCurrent(self->egl_display, self->egl_surface, self->egl_surface, + self->egl_context)) + g_warning("Failed to make EGL context current"); + + return true; +} + +static bool fl_view_gl_clear_current(void* user_data) { + FlView* self = static_cast(user_data); + + if (!eglMakeCurrent(self->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)) + g_warning("Failed to make EGL context current"); + + return true; +} + +static uint32_t fl_view_gl_fbo_callback(void* user_data) { + /* There is only one frame buffer object - always return that */ + return 0; +} + +static bool fl_view_gl_present(void* user_data) { + FlView* self = static_cast(user_data); + + if (!eglSwapBuffers(self->egl_display, self->egl_surface)) + g_warning("Failed to swap EGL buffers"); + + return true; +} + +static gboolean run_flutter_engine(FlView* self) { + FlutterRendererConfig config = {}; + config.type = kOpenGL; + config.open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig); + config.open_gl.gl_proc_resolver = fl_view_gl_proc_resolver; + config.open_gl.make_current = fl_view_gl_make_current; + config.open_gl.clear_current = fl_view_gl_clear_current; + config.open_gl.fbo_callback = fl_view_gl_fbo_callback; + config.open_gl.present = fl_view_gl_present; + + FlutterProjectArgs args = {}; + args.struct_size = sizeof(FlutterProjectArgs); + args.assets_path = fl_dart_project_get_assets_path(self->flutter_project); + args.icu_data_path = fl_dart_project_get_icu_data_path(self->flutter_project); + + FlutterEngineResult result = FlutterEngineInitialize( + FLUTTER_ENGINE_VERSION, &config, &args, self, &self->flutter_engine); + if (result != kSuccess) + return FALSE; + + result = FlutterEngineRunInitialized(self->flutter_engine); + if (result != kSuccess) + return FALSE; + + return TRUE; +} + +static void fl_view_set_property(GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) { + FlView* self = FL_VIEW(object); + + switch (prop_id) { + case PROP_FLUTTER_PROJECT: + g_set_object(&self->flutter_project, + static_cast(g_value_get_object(value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void fl_view_get_property(GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) { + FlView* self = FL_VIEW(object); + + switch (prop_id) { + case PROP_FLUTTER_PROJECT: + g_value_set_object(value, self->flutter_project); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void fl_view_dispose(GObject* object) { + FlView* self = FL_VIEW(object); + + FlutterEngineDeinitialize(self->flutter_engine); + FlutterEngineShutdown(self->flutter_engine); + + if (!eglDestroyContext(self->egl_display, self->egl_context)) + g_warning("Failed to destroy EGL context"); + if (!eglDestroySurface(self->egl_display, self->egl_surface)) + g_warning("Failed to destroy EGL surface"); + if (!eglTerminate(self->egl_display)) + g_warning("Failed to terminate EGL display"); + + g_clear_object(&self->flutter_project); + + G_OBJECT_CLASS(fl_view_parent_class)->dispose(object); +} + +static void fl_view_realize(GtkWidget* widget) { + FlView* self = FL_VIEW(widget); + + gtk_widget_set_realized(widget, TRUE); + + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + + GdkWindowAttr window_attributes; + window_attributes.window_type = GDK_WINDOW_CHILD; + window_attributes.x = allocation.x; + window_attributes.y = allocation.y; + window_attributes.width = allocation.width; + window_attributes.height = allocation.height; + window_attributes.wclass = GDK_INPUT_OUTPUT; + window_attributes.visual = gtk_widget_get_visual(widget); + window_attributes.event_mask = + gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK; + + gint window_attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; + + GdkWindow* window = + gdk_window_new(gtk_widget_get_parent_window(widget), &window_attributes, + window_attributes_mask); + gtk_widget_register_window(widget, window); + gtk_widget_set_window(widget, window); + + if (initialize_egl(self)) + run_flutter_engine(self); +} + +static void fl_view_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) { + FlView* self = FL_VIEW(widget); + + gtk_widget_set_allocation(widget, allocation); + + if (gtk_widget_get_realized(widget) && gtk_widget_get_has_window(widget)) + gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, + allocation->y, allocation->width, + allocation->height); + + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(FlutterWindowMetricsEvent); + event.width = allocation->width; + event.height = allocation->height; + event.pixel_ratio = + 1; // TODO(robert-ancell): This won't work on hidpi displays + FlutterEngineSendWindowMetricsEvent(self->flutter_engine, &event); +} + +static void fl_view_class_init(FlViewClass* klass) { + G_OBJECT_CLASS(klass)->set_property = fl_view_set_property; + G_OBJECT_CLASS(klass)->get_property = fl_view_get_property; + G_OBJECT_CLASS(klass)->dispose = fl_view_dispose; + GTK_WIDGET_CLASS(klass)->realize = fl_view_realize; + GTK_WIDGET_CLASS(klass)->size_allocate = fl_view_size_allocate; + + g_object_class_install_property( + G_OBJECT_CLASS(klass), PROP_FLUTTER_PROJECT, + g_param_spec_object( + "flutter-project", "flutter-project", "Flutter project in use", + fl_dart_project_get_type(), + static_cast(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS))); +} + +static void fl_view_init(FlView* self) {} + +/** + * fl_view_new: + * @project: The project to show. + * + * Create a widget to show Flutter application. + * + * Returns: a new #FlView + */ +G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) { + return static_cast( + g_object_new(fl_view_get_type(), "flutter-project", project, nullptr)); +} diff --git a/shell/platform/linux/public/flutter_linux/fl_dart_project.h b/shell/platform/linux/public/flutter_linux/fl_dart_project.h new file mode 100644 index 0000000000000..d2a62b56b72f9 --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_dart_project.h @@ -0,0 +1,27 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_DART_PROJECT_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_DART_PROJECT_H_ + +#include + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlDartProject, fl_dart_project, FL, DART_PROJECT, GObject) + +FlDartProject* fl_dart_project_new(const gchar* assets_path, + const gchar* icu_data_path); + +const gchar* fl_dart_project_get_assets_path(FlDartProject* project); + +const gchar* fl_dart_project_get_icu_data_path(FlDartProject* project); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_DART_PROJECT_H_ diff --git a/shell/platform/linux/public/flutter_linux/fl_view.h b/shell/platform/linux/public/flutter_linux/fl_view.h new file mode 100644 index 0000000000000..a47733e251747 --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/fl_view.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_VIEW_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FL_VIEW_H_ + +#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include "fl_dart_project.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlView, fl_view, FL, VIEW, GtkWidget) + +FlView* fl_view_new(FlDartProject* project); + +G_END_DECLS + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_VIEW_H_ diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h new file mode 100644 index 0000000000000..b63b05c24a073 --- /dev/null +++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h @@ -0,0 +1,15 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FLUTTER_LINUX_H_ +#define FLUTTER_SHELL_PLATFORM_LINUX_FLUTTER_LINUX_H_ + +#define __FLUTTER_LINUX_INSIDE__ + +#include +#include + +#undef __FLUTTER_LINUX_INSIDE__ + +#endif // FLUTTER_SHELL_PLATFORM_LINUX_FLUTTER_LINUX_H_ From 7e8ab916d51253fd49390a6f6412c3115cdab0d3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 24 Mar 2020 16:31:02 -0400 Subject: [PATCH 117/386] Roll src/third_party/dart 8246621d4da3..62f84880ef15 (3 commits) (#17290) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 3a41aba4230c9..6e3be115603ce 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '8246621d4da394bd006202bc5b1122b69fd3a4cb', + 'dart_revision': '62f84880ef15de299d826a39a20b48b8dd497816', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index a0a82a81992c1..661f33f2de217 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f552bc1ed946277be9abd4cfcd6b9499 +Signature: 6f5106681d237e2b200b77f91e114c1e UNUSED LICENSES: From 14ab32f2ef6b491d2ece84ea0766a9cbff3aa992 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 24 Mar 2020 16:36:05 -0400 Subject: [PATCH 118/386] Roll fuchsia/sdk/core/mac-amd64 from gkIC0... to Lzj3Z... (#17293) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 6e3be115603ce..af1cb6d3e6434 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'gkIC0axhhXCWFmu6PmhcVlRtJDk0Q1F6Gu15c8kx05IC' + 'version': 'Lzj3ZGYb0b41hEejAnPUyKdem5OZ6ducD5MkHB70eFwC' } ], 'condition': 'host_os == "mac"', From 1b5c28004f1b681971ed356e6dd2b79aef2e4c7f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 24 Mar 2020 16:41:03 -0400 Subject: [PATCH 119/386] Roll fuchsia/sdk/core/linux-amd64 from y2Piw... to tFo5u... (#17294) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index af1cb6d3e6434..523d3a593fb92 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'y2PiwNfLWibXm32tDyDeW0kxPcRcGlmMgqxWk32e984C' + 'version': 'tFo5u0C7qYIH67r0WUzb4QqxBu8F9YqJSEBCAicTiuYC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 09e74ac82e574..8ac52cbe98034 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: b74c2f7b452bd65c8c7b53636e06ac5a +Signature: d04347b220b4d7cc3e65c644cc7a76f1 UNUSED LICENSES: @@ -461,6 +461,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castauth/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/meta.json @@ -1372,6 +1373,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castauth/cast_auth.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castauth/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/cast_config.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/cast_setup.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/cast_system_info.fidl @@ -2197,6 +2199,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera2/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castauth/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/meta.json @@ -3252,6 +3255,7 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/noop_view_p FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/view_provider.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/module_interceptor.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/module_with_view_provider_impl.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/launch.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/power.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/update.dart FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.a2dp/audio_mode.fidl @@ -3259,6 +3263,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.bluetooth.le/connection_options.fi FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/device.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/device_watcher.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/stream.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/remote_control.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/server.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/constants.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/error.fidl From 61cda39c006ba035a7cb0109c5329ae7d3070123 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 24 Mar 2020 16:46:03 -0400 Subject: [PATCH 120/386] Roll src/third_party/skia e5ca6ba2c84c..4e79b6730dfc (1 commits) (#17295) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 523d3a593fb92..4afdae52d4277 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e5ca6ba2c84c029ae3ce15a141927d6f621311ae', + 'skia_revision': '4e79b6730dfce84f2d115ad3babf7b848d44a2b5', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f159612870328..e40841a0a7a1e 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f09cc62874b1c25b9fcd3eeae57e277e +Signature: 433a244e2b9998e72d36a3da459c7236 UNUSED LICENSES: @@ -2878,7 +2878,7 @@ FILE: ../../../third_party/skia/include/effects/SkRuntimeEffect.h FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h FILE: ../../../third_party/skia/include/private/GrGLTypesPriv.h FILE: ../../../third_party/skia/include/private/SkThreadAnnotations.h -FILE: ../../../third_party/skia/modules/canvaskit/WasmAliases.h +FILE: ../../../third_party/skia/modules/canvaskit/WasmCommon.h FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/particles_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/skottie_bindings.cpp From af440db830c78479b59bb9f128708b858074ab40 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Tue, 24 Mar 2020 15:37:33 -0700 Subject: [PATCH 121/386] [web] use web drivers as a library only. do not fetch/clone web_installers (#17299) * use web drivers as a library only. do not fetch/clone web_installers * fixing comments * addressing reviewer comments * adding a trim to output. otherwise last fails --- lib/web_ui/dev/chrome_installer.dart | 61 ++++++++++++++++++ lib/web_ui/dev/driver_version.yaml | 12 ++++ lib/web_ui/dev/integration_tests_manager.dart | 62 +++---------------- lib/web_ui/pubspec.yaml | 2 +- 4 files changed, 82 insertions(+), 55 deletions(-) create mode 100644 lib/web_ui/dev/driver_version.yaml diff --git a/lib/web_ui/dev/chrome_installer.dart b/lib/web_ui/dev/chrome_installer.dart index e761446545e29..95a3892581b38 100644 --- a/lib/web_ui/dev/chrome_installer.dart +++ b/lib/web_ui/dev/chrome_installer.dart @@ -227,3 +227,64 @@ Future fetchLatestChromeVersion() async { client.close(); } } + +/// Get the Chrome Driver version for the system Chrome. +// TODO(nurhan): https://github.com/flutter/flutter/issues/53179 +Future queryChromeDriverVersion() async { + final int chromeVersion = await _querySystemChromeMajorVersion(); + final io.File lockFile = io.File( + path.join(environment.webUiRootDir.path, 'dev', 'driver_version.yaml')); + YamlMap _configuration = loadYaml(lockFile.readAsStringSync()) as YamlMap; + final String chromeDriverVersion = + _configuration['chrome'][chromeVersion] as String; + return chromeDriverVersion; +} + +Future _querySystemChromeMajorVersion() async { + String chromeExecutable = ''; + if (io.Platform.isLinux) { + chromeExecutable = 'google-chrome'; + } else if (io.Platform.isMacOS) { + chromeExecutable = await _findChromeExecutableOnMac(); + } else { + throw UnimplementedError('Web installers only work on Linux and Mac.'); + } + + final io.ProcessResult versionResult = + await io.Process.run('$chromeExecutable', ['--version']); + + if (versionResult.exitCode != 0) { + throw Exception('Failed to locate system Chrome.'); + } + // The output looks like: Google Chrome 79.0.3945.36. + final String output = versionResult.stdout as String; + + print('INFO: chrome version in use $output'); + + // Version number such as 79.0.3945.36. + try { + final String versionAsString = output.trim().split(' ').last; + final String majorVersion = versionAsString.split('.')[0]; + return int.parse(majorVersion); + } catch (e) { + throw Exception( + 'Was expecting a version of the form Google Chrome 79.0.3945.36., ' + 'received $output'); + } +} + +/// Find Google Chrome App on Mac. +Future _findChromeExecutableOnMac() async { + io.Directory chromeDirectory = io.Directory('/Applications') + .listSync() + .whereType() + .firstWhere( + (d) => path.basename(d.path).endsWith('Chrome.app'), + orElse: () => throw Exception('Failed to locate system Chrome'), + ); + + final io.File chromeExecutableDir = io.File( + path.join(chromeDirectory.path, 'Contents', 'MacOS', 'Google Chrome')); + + return chromeExecutableDir.path; +} diff --git a/lib/web_ui/dev/driver_version.yaml b/lib/web_ui/dev/driver_version.yaml new file mode 100644 index 0000000000000..5515e1fbca7b0 --- /dev/null +++ b/lib/web_ui/dev/driver_version.yaml @@ -0,0 +1,12 @@ + +## Map for driver versions to use for each browser version. +## See: https://chromedriver.chromium.org/downloads +chrome: + 81: '81.0.4044.69' + 80: '80.0.3987.106' + 79: '79.0.3945.36' + 78: '78.0.3904.105' + 77: '77.0.3865.40' + 76: '76.0.3809.126' + 75: '75.0.3770.140' + 74: '74.0.3729.6' diff --git a/lib/web_ui/dev/integration_tests_manager.dart b/lib/web_ui/dev/integration_tests_manager.dart index bd97ec878a7ef..6e5457da4fe0b 100644 --- a/lib/web_ui/dev/integration_tests_manager.dart +++ b/lib/web_ui/dev/integration_tests_manager.dart @@ -5,7 +5,9 @@ import 'dart:io' as io; import 'package:path/path.dart' as pathlib; import 'package:web_driver_installer/chrome_driver_installer.dart'; +import 'package:yaml/yaml.dart'; +import 'chrome_installer.dart'; import 'common.dart'; import 'environment.dart'; import 'utils.dart'; @@ -46,24 +48,6 @@ class IntegrationTestsManager { } } - void _cloneWebInstallers() async { - final int exitCode = await runProcess( - 'git', - [ - 'clone', - 'https://github.com/flutter/web_installers.git', - ], - workingDirectory: _browserDriverDir.path, - ); - - if (exitCode != 0) { - io.stderr.writeln('ERROR: ' - 'Failed to clone web installers. Exited with exit code $exitCode'); - throw DriverException('ERROR: ' - 'Failed to clone web installers. Exited with exit code $exitCode'); - } - } - Future _runPubGet(String workingDirectory) async { final String executable = isCirrus ? environment.pubExecutable : 'flutter'; final List arguments = isCirrus @@ -90,34 +74,14 @@ class IntegrationTestsManager { } void _runDriver() async { - final int exitCode = await runProcess( - environment.dartExecutable, - [ - 'lib/web_driver_installer.dart', - '${_browser}driver', - '--install-only', - ], - workingDirectory: pathlib.join( - _browserDriverDir.path, 'web_installers', 'packages', 'web_drivers'), - ); - - if (exitCode != 0) { - io.stderr.writeln( - 'ERROR: Failed to run driver. Exited with exit code $exitCode'); - throw DriverException( - 'ERROR: Failed to run driver. Exited with exit code $exitCode'); - } startProcess( './chromedriver/chromedriver', ['--port=4444'], - workingDirectory: pathlib.join( - _browserDriverDir.path, 'web_installers', 'packages', 'web_drivers'), ); print('INFO: Driver started'); } void prepareDriver() async { - final io.Directory priorCurrentDirectory = io.Directory.current; if (_browserDriverDir.existsSync()) { _browserDriverDir.deleteSync(recursive: true); } @@ -125,22 +89,12 @@ class IntegrationTestsManager { _browserDriverDir.createSync(recursive: true); temporaryDirectories.add(_drivers); - // TODO(nurhan): We currently need git clone for getting the driver lock - // file. Remove this after making changes in web_installers. - await _cloneWebInstallers(); - // Change the directory to the driver_lock.yaml file's directory. - io.Directory.current = pathlib.join( - _browserDriverDir.path, 'web_installers', 'packages', 'web_drivers'); - // Chrome is the only browser supporting integration tests for now. - ChromeDriverInstaller chromeDriverInstaller = ChromeDriverInstaller(); - bool installation = await chromeDriverInstaller.install(); - - if (installation) { - io.Directory.current = priorCurrentDirectory; - await _runDriver(); - } else { - throw DriverException('ERROR: Installing driver failed'); - } + // TODO(nurhan): https://github.com/flutter/flutter/issues/53179 + final String chromeDriverVersion = await queryChromeDriverVersion(); + ChromeDriverInstaller chromeDriverInstaller = + ChromeDriverInstaller.withVersion(chromeDriverVersion); + await chromeDriverInstaller.install(alwaysInstall: true); + await _runDriver(); } /// Runs all the web tests under e2e_tests/web. diff --git a/lib/web_ui/pubspec.yaml b/lib/web_ui/pubspec.yaml index 997e119224c72..75743250398be 100644 --- a/lib/web_ui/pubspec.yaml +++ b/lib/web_ui/pubspec.yaml @@ -25,4 +25,4 @@ dev_dependencies: git: url: git://github.com/flutter/web_installers.git path: packages/web_drivers/ - ref: dae38d8839cc39f997fb4229f1382680b8758b4f + ref: 90c69a79b2764c93875dc8ed4f4932c27a6f3a86 From 5fddb9be1c20d47adabb0d5e85fee2def0486ae8 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Tue, 24 Mar 2020 15:54:40 -0700 Subject: [PATCH 122/386] Add empty string check to Locale toString (#17280) --- lib/ui/window.dart | 4 ++-- testing/dart/locale_test.dart | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index c830b6f4c215c..d52bfea1a6c3c 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -508,9 +508,9 @@ class Locale { String _rawToString(String separator) { final StringBuffer out = StringBuffer(languageCode); - if (scriptCode != null) + if (scriptCode != null && scriptCode.isNotEmpty) out.write('$separator$scriptCode'); - if (_countryCode != null) + if (_countryCode != null && _countryCode.isNotEmpty) out.write('$separator$countryCode'); return out.toString(); } diff --git a/testing/dart/locale_test.dart b/testing/dart/locale_test.dart index 5f1f83780732a..5bebd5106be6d 100644 --- a/testing/dart/locale_test.dart +++ b/testing/dart/locale_test.dart @@ -53,4 +53,10 @@ void main() { expect(const Locale.fromSubtags(languageCode: 'en').hashCode, isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn').hashCode)); }); + + test('Locale toString does not include separator for \'\'', () { + expect(const Locale('en').toString(), 'en'); + expect(const Locale('en', '').toString(), 'en'); + expect(const Locale('en', 'US').toString(), 'en_US'); + }); } From e2d9c6039344d8cec1444ac4fa2b8d0f95705adb Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Tue, 24 Mar 2020 20:10:10 -0700 Subject: [PATCH 123/386] Flush background canvas before allocating a new surface (#17308) --- .../darwin/ios/framework/Source/FlutterPlatformViews.mm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b96fcbbbaa76a..32c91c91e4077 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -451,6 +451,9 @@ std::shared_ptr ios_context, SkCanvas* background_canvas) { DisposeViews(); + + // Resolve all pending GPU operations before allocating a new surface. + background_canvas->flush(); // Clipping the background canvas before drawing the picture recorders requires to // save and restore the clip context. SkAutoCanvasRestore save(background_canvas, /*doSave=*/true); From cd974c347160cdf0f0589735b91362bf3f89085a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 00:11:02 -0400 Subject: [PATCH 124/386] Roll src/third_party/skia 4e79b6730dfc..6beb1dd941af (17 commits) (#17307) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/DEPS b/DEPS index 4afdae52d4277..74a0145e289fe 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4e79b6730dfce84f2d115ad3babf7b848d44a2b5', + 'skia_revision': '6beb1dd941afe8ded876a25bee21b529d95ead30', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index e40841a0a7a1e..794bda9a00f45 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 433a244e2b9998e72d36a3da459c7236 +Signature: 9590eced250fca00e288000c0c9e18e8 UNUSED LICENSES: @@ -2878,7 +2878,7 @@ FILE: ../../../third_party/skia/include/effects/SkRuntimeEffect.h FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h FILE: ../../../third_party/skia/include/private/GrGLTypesPriv.h FILE: ../../../third_party/skia/include/private/SkThreadAnnotations.h -FILE: ../../../third_party/skia/modules/canvaskit/WasmCommon.h +FILE: ../../../third_party/skia/modules/canvaskit/WasmAliases.h FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/particles_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/skottie_bindings.cpp @@ -3912,11 +3912,13 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.h -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResource.cpp -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResource.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceState.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DSurfaceResource.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DSurfaceResource.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTypesPriv.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.h @@ -6129,7 +6131,6 @@ FILE: ../../../third_party/skia/src/core/SkImageInfo.cpp FILE: ../../../third_party/skia/src/core/SkRasterClip.cpp FILE: ../../../third_party/skia/src/core/SkRasterClip.h FILE: ../../../third_party/skia/src/core/SkStrikeCache.h -FILE: ../../../third_party/skia/src/gpu/GrAllocator.h FILE: ../../../third_party/skia/src/gpu/GrBufferAllocPool.cpp FILE: ../../../third_party/skia/src/gpu/GrBufferAllocPool.h FILE: ../../../third_party/skia/src/gpu/GrClip.h @@ -6137,6 +6138,7 @@ FILE: ../../../third_party/skia/src/gpu/GrColor.h FILE: ../../../third_party/skia/src/gpu/GrFixedClip.cpp FILE: ../../../third_party/skia/src/gpu/GrGlyph.h FILE: ../../../third_party/skia/src/gpu/GrGpu.cpp +FILE: ../../../third_party/skia/src/gpu/GrTAllocator.h FILE: ../../../third_party/skia/src/gpu/GrVertexWriter.h FILE: ../../../third_party/skia/src/gpu/SkGpuDevice.h FILE: ../../../third_party/skia/src/gpu/SkGr.cpp From bb35963050aaa8edd9f7ef5dd30384098aacf96f Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 24 Mar 2020 21:42:12 -0700 Subject: [PATCH 125/386] Add service protocol to get SkSLs (#17300) Fixes https://github.com/flutter/flutter/issues/53114 --- runtime/service_protocol.cc | 3 ++ runtime/service_protocol.h | 1 + shell/common/persistent_cache.cc | 2 +- shell/common/persistent_cache.h | 8 +++++ shell/common/shell.cc | 31 ++++++++++++++++++ shell/common/shell.h | 7 ++++ shell/common/shell_test.cc | 14 ++++++++ shell/common/shell_test.h | 8 +++++ shell/common/shell_unittests.cc | 55 ++++++++++++++++++++++++++++++++ 9 files changed, 128 insertions(+), 1 deletion(-) diff --git a/runtime/service_protocol.cc b/runtime/service_protocol.cc index 16d574de8f8c3..5c7ea0279a4a7 100644 --- a/runtime/service_protocol.cc +++ b/runtime/service_protocol.cc @@ -33,6 +33,8 @@ const std::string_view ServiceProtocol::kSetAssetBundlePathExtensionName = "_flutter.setAssetBundlePath"; const std::string_view ServiceProtocol::kGetDisplayRefreshRateExtensionName = "_flutter.getDisplayRefreshRate"; +const std::string_view ServiceProtocol::kGetSkSLsExtensionName = + "_flutter.getSkSLs"; static constexpr std::string_view kViewIdPrefx = "_flutterView/"; static constexpr std::string_view kListViewsExtensionName = @@ -50,6 +52,7 @@ ServiceProtocol::ServiceProtocol() kFlushUIThreadTasksExtensionName, kSetAssetBundlePathExtensionName, kGetDisplayRefreshRateExtensionName, + kGetSkSLsExtensionName, }), handlers_mutex_(fml::SharedMutex::Create()) {} diff --git a/runtime/service_protocol.h b/runtime/service_protocol.h index ce2a6845c24b4..c66a836a2c52e 100644 --- a/runtime/service_protocol.h +++ b/runtime/service_protocol.h @@ -27,6 +27,7 @@ class ServiceProtocol { static const std::string_view kFlushUIThreadTasksExtensionName; static const std::string_view kSetAssetBundlePathExtensionName; static const std::string_view kGetDisplayRefreshRateExtensionName; + static const std::string_view kGetSkSLsExtensionName; class Handler { public: diff --git a/shell/common/persistent_cache.cc b/shell/common/persistent_cache.cc index 66690971ace46..f663b081bad99 100644 --- a/shell/common/persistent_cache.cc +++ b/shell/common/persistent_cache.cc @@ -24,7 +24,7 @@ std::string PersistentCache::cache_base_path_; std::mutex PersistentCache::instance_mutex_; std::unique_ptr PersistentCache::gPersistentCache; -static std::string SkKeyToFilePath(const SkData& data) { +std::string PersistentCache::SkKeyToFilePath(const SkData& data) { if (data.data() == nullptr || data.size() == 0) { return ""; } diff --git a/shell/common/persistent_cache.h b/shell/common/persistent_cache.h index 35d8f08f8598a..c48627fdbfcfa 100644 --- a/shell/common/persistent_cache.h +++ b/shell/common/persistent_cache.h @@ -32,8 +32,16 @@ class PersistentCache : public GrContextOptions::PersistentCache { static PersistentCache* GetCacheForProcess(); static void ResetCacheForProcess(); + // This must be called before |GetCacheForProcess|. Otherwise, it won't + // affect the cache directory returned by |GetCacheForProcess|. static void SetCacheDirectoryPath(std::string path); + // Convert a binary SkData key into a Base32 encoded string. + // + // This is used to specify persistent cache filenames and service protocol + // json keys. + static std::string SkKeyToFilePath(const SkData& data); + ~PersistentCache() override; void AddWorkerTaskRunner(fml::RefPtr task_runner); diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 8dc5cbed29f7c..d5132c6e4c38b 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -30,6 +30,7 @@ #include "rapidjson/writer.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/skia/include/core/SkGraphics.h" +#include "third_party/skia/include/utils/SkBase64.h" #include "third_party/tonic/common/log.h" namespace flutter { @@ -373,6 +374,10 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) task_runners_.GetUITaskRunner(), std::bind(&Shell::OnServiceProtocolGetDisplayRefreshRate, this, std::placeholders::_1, std::placeholders::_2)}; + service_protocol_handlers_[ServiceProtocol::kGetSkSLsExtensionName] = { + task_runners_.GetIOTaskRunner(), + std::bind(&Shell::OnServiceProtocolGetSkSLs, this, std::placeholders::_1, + std::placeholders::_2)}; } Shell::~Shell() { @@ -1341,6 +1346,32 @@ bool Shell::OnServiceProtocolGetDisplayRefreshRate( return true; } +bool Shell::OnServiceProtocolGetSkSLs( + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + FML_DCHECK(task_runners_.GetIOTaskRunner()->RunsTasksOnCurrentThread()); + response.SetObject(); + response.AddMember("type", "GetSkSLs", response.GetAllocator()); + + rapidjson::Value shaders_json(rapidjson::kObjectType); + PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); + std::vector sksls = persistent_cache->LoadSkSLs(); + for (const auto& sksl : sksls) { + size_t b64_size = + SkBase64::Encode(sksl.second->data(), sksl.second->size(), nullptr); + sk_sp b64_data = SkData::MakeUninitialized(b64_size + 1); + char* b64_char = static_cast(b64_data->writable_data()); + SkBase64::Encode(sksl.second->data(), sksl.second->size(), b64_char); + b64_char[b64_size] = 0; // make it null terminated for printing + rapidjson::Value shader_value(b64_char, response.GetAllocator()); + rapidjson::Value shader_key(PersistentCache::SkKeyToFilePath(*sksl.first), + response.GetAllocator()); + shaders_json.AddMember(shader_key, shader_value, response.GetAllocator()); + } + response.AddMember("SkSLs", shaders_json, response.GetAllocator()); + return true; +} + // Service protocol handler bool Shell::OnServiceProtocolSetAssetBundlePath( const ServiceProtocol::Handler::ServiceProtocolMap& params, diff --git a/shell/common/shell.h b/shell/common/shell.h index d768c0673b973..199cfe1cb44ee 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -561,6 +561,13 @@ class Shell final : public PlatformView::Delegate, const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response); + // Service protocol handler + // + // The returned SkSLs are base64 encoded. Decode before storing them to files. + bool OnServiceProtocolGetSkSLs( + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + fml::WeakPtrFactory weak_factory_; // For accessing the Shell via the GPU thread, necessary for various diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 1267876fed44f..c96aece85d734 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -202,6 +202,20 @@ bool ShellTest::GetNeedsReportTimings(Shell* shell) { return shell->needs_report_timings_; } +void ShellTest::OnServiceProtocolGetSkSLs( + Shell* shell, + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response) { + std::promise finished; + fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetIOTaskRunner(), + [shell, params, &response, &finished]() { + shell->OnServiceProtocolGetSkSLs( + params, response); + finished.set_value(true); + }); + finished.get_future().wait(); +} + std::shared_ptr ShellTest::GetFontCollection( Shell* shell) { return shell->weak_engine_->GetFontCollection().GetFontCollection(); diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 9bd9f7fd39607..7d35853cdbec6 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -72,6 +72,14 @@ class ShellTest : public ThreadTest { static bool GetNeedsReportTimings(Shell* shell); static void SetNeedsReportTimings(Shell* shell, bool value); + // Helper method to test private method Shell::OnServiceProtocolGetSkSLs. + // (ShellTest is a friend class of Shell.) We'll also make sure that it is + // running on the UI thread. + static void OnServiceProtocolGetSkSLs( + Shell* shell, + const ServiceProtocol::Handler::ServiceProtocolMap& params, + rapidjson::Document& response); + std::shared_ptr GetFontCollection(Shell* shell); // Do not assert |UnreportedTimingsCount| to be positive in any tests. diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 26963e1c146be..a39ea3aac6758 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -19,6 +19,7 @@ #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_test.h" @@ -26,7 +27,9 @@ #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiter_fallback.h" +#include "flutter/shell/version/version.h" #include "flutter/testing/testing.h" +#include "rapidjson/writer.h" #include "third_party/tonic/converter/dart_converter.h" namespace flutter { @@ -1129,5 +1132,57 @@ TEST_F(ShellTest, CanDecompressImageFromAsset) { DestroyShell(std::move(shell)); } +TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) { + // Create 2 dummpy SkSL cache file IE (base32 encoding of A), II (base32 + // encoding of B) with content x and y. + fml::ScopedTemporaryDirectory temp_dir; + PersistentCache::SetCacheDirectoryPath(temp_dir.path()); + PersistentCache::ResetCacheForProcess(); + std::vector components = {"flutter_engine", + GetFlutterEngineVersion(), "skia", + GetSkiaVersion(), "sksl"}; + auto sksl_dir = fml::CreateDirectory(temp_dir.fd(), components, + fml::FilePermission::kReadWrite); + const std::string x = "x"; + const std::string y = "y"; + auto x_data = std::make_unique( + std::vector{x.begin(), x.end()}); + auto y_data = std::make_unique( + std::vector{y.begin(), y.end()}); + ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "IE", *x_data)); + ASSERT_TRUE(fml::WriteAtomically(sksl_dir, "II", *y_data)); + + Settings settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + ServiceProtocol::Handler::ServiceProtocolMap empty_params; + rapidjson::Document document; + OnServiceProtocolGetSkSLs(shell.get(), empty_params, document); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + DestroyShell(std::move(shell)); + + // Base64 encoding of x, y are eQ, eA. + ASSERT_STREQ( + buffer.GetString(), + "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"II\":\"eQ==\",\"IE\":\"eA==\"}}"); + + // Cleanup files + fml::FileVisitor recursive_cleanup = [&recursive_cleanup]( + const fml::UniqueFD& directory, + const std::string& filename) { + if (fml::IsDirectory(directory, filename.c_str())) { + fml::UniqueFD sub_dir = + OpenDirectoryReadOnly(directory, filename.c_str()); + VisitFiles(sub_dir, recursive_cleanup); + fml::UnlinkDirectory(directory, filename.c_str()); + } else { + fml::UnlinkFile(directory, filename.c_str()); + } + return true; + }; + VisitFiles(temp_dir.fd(), recursive_cleanup); +} + } // namespace testing } // namespace flutter From 945967054bcb6339b273bcc45874f9a5f154763b Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 24 Mar 2020 21:43:16 -0700 Subject: [PATCH 126/386] Rename GPU to raster thread in PerformanceOverlay (#17148) Rename GPU to raster thread in PerformanceOverlay and update goldens. --- flow/layers/performance_overlay_layer.cc | 2 +- .../performance_overlay_layer_unittests.cc | 2 +- .../performance_overlay_gold_120fps.png | Bin 16355 -> 16287 bytes .../performance_overlay_gold_60fps.png | Bin 16572 -> 16560 bytes .../performance_overlay_gold_90fps.png | Bin 16359 -> 16293 bytes 5 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc index ef7b6f2c6194c..dacb9428604f8 100644 --- a/flow/layers/performance_overlay_layer.cc +++ b/flow/layers/performance_overlay_layer.cc @@ -89,7 +89,7 @@ void PerformanceOverlayLayer::Paint(PaintContext& context) const { VisualizeStopWatch( *context.leaf_nodes_canvas, context.raster_time, x, y, width, height - padding, options_ & kVisualizeRasterizerStatistics, - options_ & kDisplayRasterizerStatistics, "GPU", font_path_); + options_ & kDisplayRasterizerStatistics, "Raster", font_path_); VisualizeStopWatch(*context.leaf_nodes_canvas, context.ui_time, x, y + height, width, height - padding, diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 769f80803a8fa..cf0cd68853e59 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -163,7 +163,7 @@ TEST_F(PerformanceOverlayLayerTest, SimpleRasterizerStatistics) { layer->Paint(paint_context()); auto overlay_text = PerformanceOverlayLayer::MakeStatisticsText( - paint_context().raster_time, "GPU", ""); + paint_context().raster_time, "Raster", ""); auto overlay_text_data = overlay_text->serialize(SkSerialProcs{}); SkPaint text_paint; text_paint.setColor(SK_ColorGRAY); diff --git a/testing/resources/performance_overlay_gold_120fps.png b/testing/resources/performance_overlay_gold_120fps.png index 5027397e80482a4e072bf2812990ab341b1c8245..c19d1eb7208e1ee768b0031404592fa26e65ffd5 100644 GIT binary patch literal 16287 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�}d#?vw^uxJ9Q7oUVFRllrr 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 WWnui`xANj&UpQ-Zroz5fB+=3IUQ>icrW95g{^# zQUqiQ0y0A)AVdfd<}fBQk4cyV8Q)2Dt?sVwx?T77>+ZMK>zlP$S;?1tIcJ}J_SyUY z+eBY8GZx)*V2_ZHkm%)0=WhrJ?VJ<*34aORG0p0m0#APlFuH6h3?AXacYhWV`cCNb z`LmWG=}TiC&#cHlDQ&GKkG4J;9eQrMls0(w3r}>cw8>%hY56$9X$$%>RPB-buOaKR zyuRxTZ+X`oU7;K2M;tNla!o3{Je3t~zZpJbl+!inL(SbX`z z$)C_4lGfQ5j(%Gg#;A6O9bHGxQUZ*?c>beDm8I7k$)_!Q#)9IiHCJwL$ZeMRz% z8M}*E+QB@r>vM$ex!i=&x#0MVkXgHyj(v*`g#6mstViSV=9!vB2I$otLJvvz@88eg zPY>~b^M$lJWlVo`B~G?!k!+Wj=G~F%hs3&04R0@bvweGrNU^9tulz?Ev=w;xB>Vy`P14{Oe0w zMqXtt=qS}SaLF)p{~vtfKlt+hcbC3{6)1n0vn@L^3i+qC(<)HAe)@*Og;!!NUkM4} zQrrLf0{nh$bbQO)h)01@KV|&RsNZS!eAV16!t$>^3x`A9)yPp2MxLIRJ=B={=2-;^ zo_hY?O&wK)d+QchpFjvIQg#ew-MhP*lJ!@7ZR=;oFX~QpwL0iMu`POY)wo&1o(=0m z1o;LTJzvh+N-`az2p` zcy;F_!%x*2X?J=f^+pE=w?@+2K-@7rBrS`GA3a0|BbL~)d3IK0nt1#i822*lZ{9|K zw8;NmCU3QO+)*`{dU~8^?il2T4SG5BQJSExLJixU$v3bbWr%_=6Dr=ic;# zZ_utS|0-HV$8>M5Z_%YAyC;hAyrwKJ_wc;Eqmoa$7A3@`0b>v*Eh;89!~eV+vZWv; zB~@QpV+PE6LF@hw_|o9aqv`-^zKS};RwBmL#lT?tag?~aj!ffo2_xb8KwVxXl3fX@ znwn{yI%8`)G&DTSKACG~x+tm`v0HxYLDPJ%XuV#r{y^|bwMlZFjfF)()mFbXzxYs= z)vRpZ+qv*N1Ri+uH)7HMdtx>HNN$#4) zNF_3>sIKlba&rlqA1`O`hpUG9kUyL$@iUBFSUB{g;HH5FuP@JI%)TOgaVn{kc=u!p z*(uEXx{J$XECz$=30fSeKj}-Ulhi81oRgFh_C~_5 zYFL-93$HBE$HX%_-6n_9Y|-keA^LLm?>_gZ+IUU*u$t8oD`z}CJ)73&d-=z`hu^#O z=9)u-B=fiS8yFbCR8>_)QHHEJqGCkX;Gni)#54Frv=YVqhi_8suPcG@aPmOXLqjNi zBq`4l5j@$#r+vJ(<>i?+Y=5mQV{0x8<%8G$MMg%3c{NS52tG#=kyg3K6X%aB@!3D zo|)wqSM78BKH<}w8?cm+aKAq3LLzZ72FYDSYE5^&%9aDm`!ptoG`+!;$%gQ@u)QTV zM>IVLYLj;Ds*lECRvW+`@>1B7DkY`5y*A_hHvjEC)pj?Rq4;%ye4os~8=l5#YCIF!REIcV@raU5qAZxFuXKIFB8tY8}H zDVdF040oTvGVV9Ru6I{Yeg1swtfS*tE{bbyWwr6GwDjit@^Y)Vn`{vgJ;@m9qT;+l zFSXd_(pOnoI3$xEHC$Vhua@#EQ~Lq+WpeV6qhf^LYzF?IwJogfXoQ4(*wVa*lDQd! zQaWv~q#$_J(Gj3mkRLCd-MgJ4D$Cng8D}v=7Z1j`HgUF#txR7oue=P=ms(sZln3sz zdst7mMO5~o@c~A;C*Q;9nAHmep9|rxOFcU6i8!H_c-f5_#;7fll2Y0uts0tL^M3s* z0*So5usl9OB4M~@{WBd@kLn)JJ%UdhF{fDJyhkL9%G~jeIu2uoysb%FD=VuaYst>y zN=A)|Nt3d<;r@8jj02@5Hv5q4_d@2XXN{WYd(khD%by$eTjZK?e~6GkuzE8Mhba_8 z!w3lsf;EqSsr}$iuZ6k62CLT`!j$)H>BeFKpARTSUF?-D|Mhoa{&5V;mO!t68j_ zE@B6(CYO>`%_}yUsLQGjTlZXfDhw#89W9NGPm22@{KmT{tqKqZYKhTN zz}M)};)Yu{JVm}!T3dLa%kXSlB7fL0?W(LfWuk{X58x>a&ir7x1xOL(`qVECgBA{;T>@VI~!--jpL z&o;+6=jRneY*xFCnU#1xFSFv!ja?~H$_Nd$gnhB1T1O7`=VrSGTf0uLynf}sy2_f6 zk}7%v9K7aEiI+{qQgzMw@8w2HCKD{en3RCfiI4S}dco(wwJPrB#_6IWjT$pdy5Yp5 z`|c!6Q5%WGU=xrW0%Usx&mG}UGWa|OzSg5RC#A_Tpa{y!eHiq5+yb(XD>8`k{w4qwDt(W?NI zyn(KFOYD9Hb;p`OHrVYG;FJy1mPff`!dF{m4P zqm4%?I5}Hd_HpU83`#wmy_pTAz-Gh54c5B~N^DfvEwaAh-g6xHO!%g!as0uQV6r2$ ze(j^AdEE=JR?Ced>V=Doi)Eh=3kf-g=@m6}lOE#z1XY1zXpXLn6n1_&8L=4&or~E@`Gf- zV4-1Loi+|>1TPH5Y_tX}6S>w=wE=JHnb&<3M?Kquwk z&nN{vvb{57(M(j1T0vW}0M^dTja9(oL*C>{Mi!6~4pl-sQ_J)GCqBXt8^^0Gq@MEd zy{wz@37kWfVT}>+!xt`Ov_dJ{yp@32h24?ExcmJYlf{9>LuJ(cX_-`6YWd=;g4B7_ z>>Inhy}c!+LAe+-@i96#m^OSm48$?0m9=$d+G)>$Sv3`?Djdwny`Z3}YLd|O=3JDK zu%bR^sq8kNv#ltLih89!x?3UqcrJ)-cfrl9g%!c^k@HRTdEE#J5VzD|S8g{v+NEH) z_4#qFQPsPnQg;}P5@B3hkV z6FA?~1k#S~?h^O4nGR3jrO$Wn+}YX`B~GdWK1|uC>*VZ=A16R~D@-VDxZkh9Sx_iC zOdsruGO|S=sJUlXu(b%yS0EaECy3m-oMtAT&qZSM^5*x9_gt80YikPyk++sy_!V>3 z4n<0A<(wdk?fupjWKPAt?YYXaxi_{`Y*b{QxBj%%RdGxCCMO45SEmX|Su%-OCK&gs zJJ_dE@O;nQSrW1+Co2of7kG~-l)pCXxtkikwwv(uX{VBVdkFqcf4=)7c!z`%AFC{R zaDkwdS*R;8Z|7C7f_z;kZP&KW9^y`xA0wDBxuVQF?ypKS&t2;=H6`kSFq*r-VRMI*Z|u?r}S+Bj(6*F7<&p6sH<*oH;l==GVLDO z2oqL()7z>Xf8E0)jPl-Nyx_ZMQMft=PU z$jUPL^C+w6^WP_e+miIx+II>fr2N5yDti?p23-p#9XWxfw{MT15mp4j1<5*PdiHF? z8O^jf(YBoAzOE?}3s+4Q9+*y)dDM2Dx|1pu2nQIH@Kh-3}|s$yw$lb$*&Y6u3fwK%o8!5TjKwF>X^ z1eI4@_{Q=8Fqgl#H*GrxqO7V~-Y+XSGO|`FZuTQD zFreBxyldB(JBw}7a}EVZCzXrq#cabu!-zMgr@b>_#Wf&}z~|=r_chKUQY`Q@J%(lGT^a2sh$MVLY zcyjm(ZdQ@=Ag8>x*8i8%Rh#R=1(1v<>LA(h^7Y+nXSdeZPv?PC3pX<}!;&zv5LSF& zS&3d}GG)jE1MzJWHejsLyF?Jq8?8#pq|=&MZR%jv1g9@g@5-++qN?i9P_IS8{ddjdG$BtmVpI!sw%-{US2B z8yR4K+S$2nL`WF9yZ7I6azfS>7OFBh+o=r_Mxa2k_99{o#M3EwByA`LVvplMgW&M& zJd|M#KhT%qPWC;jtgJWE({oZS;$7HW85WBzWwBT@xAmrvZM=99p(PJLRT#n?dpA|j zY<$k7h}5Fh#TW709e#+V!E4e+gP^urdNA1{_oIpm=PLJN&)}eqp0>746*xm^iAPKq zgcTzi8XD4hVHzM$*|bqf_V)0|bE@(iUwK8^y?eJL3G39$PBc2RJySQx{sJhv&uL7@$nZ@} zeSL>MQ|g%+`}Z9+BZ0$o^<%V-Bf5AW0@Ru~9`E%!t&(rjK~SIu9Z6~RE8^9^wyl5>{*a+&a|6Kjg7hNj-n`F^kM#! zug*oefwDTUzNUsX+7c&=R!VH;9X3ghZ40Xugr=uYlN+CiICrp{6zGPDU}^7rLVmoQ zy{A{;Xdscl2{e}wLcDJ{eX{@1{yb!HZaWUw6KFzg$IkawT7nqu1-1z#HMQwNO>Jf}-uH?`wsa16ahAS}{zBTMwY_Q&AqOO{z z6EM^5SR33yr_tof?leEGC-+*Tl^1BD9f?X&)}Hqp$JFr$SJ~{Q2Au$->*nT14WMdZ zuPE8s*&UD%Uv3~{b2-5G;(?REKo)=o=|~78T2d*WNgt~{n%2=0InsYy8{~_$j8h&x zN@1Hzw&`dzI@J))iUw8C3XoYGR{Jc>xxk6BYoTqsgdV=#msHfjVqNKF;Mnz&85yU! zpwhPrBFK?q)3g{J`IhPUX{2s<^=%)e24x78$Q z*DizTjx_w3|90(AR$gxI_vK;C$KI~4xqFh;`HtMG2MQ>i(Dls#MTf!o#flM4FsMpt z#CrEm8YqaDKQ9D0I=bxk#dD{JiB1Kg^MOvAAXV*senl!9tpa=H)1U7gM>V5h9_JtZ+g{l;gUG6kawdxT!8O{MSjRW4?67ajv z8yo4Qa8hxFAt7FMscxCE3mt(6x`TE~>&G%63tDRVQe^v);j4Wef3@I@(>V`GI z8%)7N!#QO931N6-#8V;U^G{TBjQ+NUxt9aV=7=P2tt|7&BOGpdDU)lv#Yb#4by?UQ zz-_N(MuDppK4YQwPEM2$k9LV*JzH=U6&0Se;d0*$oq(KBE^EUX5;)hjZd>L?1H1B& zas1xe=21UEO#?k};DF&!b-?U%cMNQ%6;v)A_7z@3(&Gn(9;!e7+FgxNr!n%9D5~kR zw-^=J8E5wcoj7%qIhg_F`Hxlyl8af#mzf?^R9Jw0qvl z+iDiU{j>T)c4BuYfbEzrUy0lAQvblhYuJx~WKnq|(F+R;n*;@9cz_xcl;*sn(lOj= zKLSmz-D>Fld|q+_>1lFX+vJnDIHKM~z4R!FMn@=@`IVqZ6qG+!H~ckyXWHYE+(~A0 z9jDWcn=|>_nfOzywm|W}S2Mn2Y)QMa4ePk*Gnl#yRGVbOfSH!IJPU}5IjD1U&CTaV z>e-80O9%n50VuCZ4BR=?-rha7?dbv#r4?g)zJCCo&-nZi2>jQ?27Ks0di?y$;3cJx zlNVCPw(ZXC80)VWHn3D)OJOkDYQ*S&^+Iom3v{Frsrq?sXR`tK0lg zbvF$V)6VkPv=ak5sQ#S6_v2zrCX=s{`zv+*xj8cP5Q_RHR50kKe?5_Z13Q1abaB*8 zbq`M1{sma1GirY`i*WwgdthtzovZwx0GQu^?4PUs|6L&Ce*~HcP{lrj-yyrh`4<g(q zCo?*yK^FF_$Nh@50PLI^j!b)i0)*aSYz`qWs#T*-y^w?ySW;&3e0e#M=q zft;i@y7D#ktaE+M>@_v1eR0sP05}Q$X0aH6{;K_q1>;wl{y|x6*s0MuL|y8?U@a<@ zU-k!B>vy((wq`iDY$f>wx&fL2)D`}74Crt5iZiP>tsjaEfHGDb8a~;w%<9M&0fy81 zdm;Wian{lO9tr{AyZhq*c+vj-Lj9K$;Q#U){)&`4%G^w=_&3R50uM>j%F%)?n3% zIt+0`F}9Aib&0tzuAk`JV&GrR?|9qP@obSfUC^^zr!qirc0{q+Y-F;#Otoj+NiV_lHwOSfTC*Bi?DmTyv^j!d0=4&PZ;QF&k8l0E&)k zkAMxLFdAe%r>wGxyjBL*9p-5Fc%L%_}G&o-nM&3vYccZXofP~ekz*~;O%f_)O_byzRl>}9pok6fyz;%7hSFtjU87 z24QqxU*F78=$hSzvGPrQd$ifCdxbCmqP>7cE)6j(<@72(hl9Kw`_oT%#>4jkm0*u` zQK%ay&!V@c(rbI5RXGEyN=3=iCRTh33Pfb(VLzwC2x!FRS6|b@O6b8xw9(sowFNebD2e?g0 zHEjYIaI+tbd1(ad%Vsl>xvoIPuMgi22x`5{H&uwbTMBV-&VKK|xlcd!dwa1jOeJkx*M)r9CAo7Fl%*bTTA-Jp}Dq z1DcUDpG7s*3B2cF>`i+$F)fc@ z>(_^olsZu>0Zth5QW@#KPQ`nYYbxmUl03nLq*d*lYXu-nFqw&!z$Ho;fAI~-JNsJV zsCo}pxO#d8y8#NO%Hy`mUfE8AvQgehSW=1pBW-RU8M-vcp(hMmSO%)4pvejh@D^m< z*4f#krnp$!(QY^ya)ZsEMC!92H#Rn6l9a7|07h6TvaiI>F&OH{e*94f31Bl0@S~*x z38SMbOIKCw-s5sgORJAd6C(vw5&+BljD`FIbyb{bNoJgfN6MI#kuZ>t@>L%B~C>k1f2t#zC1HxlKi=1@QLHFRqUz&oMsrXX>H>R!Cr+M!9wF9m?cP zjX+oc`oZVAUge9*Y8Dy-&gQKmO}u&YCMJAqJc?RWP_Vc}r+4zBkXuoZG=OQalLVAj ztmW)0#=Rqn(e+ysG0L*CvhHqWL+4?z?Rhr6*0;AW&%zwlXAaV1SLyBj>;JDf-Dl8R zQ4cop%>fML__Y_fMQo;N$W#LV)W%P{dBiuZi4YZT-QwC>*rZ|vs6+!>U$`E#de%x} z)J|ycE$9D@6zTsO==F6~2L)xm06H0d?Q=uFyD=zk_m=??x^62wRUjcMzypyWR`ESd z!0>EKCbTBMiEg-^S*DO@2sTyiF=dpDXi_4YEK>|?LyiL7kH{Y%M)>Hd+Z2@}s)C@8>5nPBvsR zS!PIK?+&5QZa;JQ5?Y#A-7wBp9bLlcXF~7o?Nw_02+#miW@f6#zzSOS!F(unWKS?} z8%V;Z-zxEmGHGym6yn^2m}x08lhE#>SE! zXCj#rZAAMu{APKkInd^Tvb_et#PG>L6@Wx6t>lz-{XWN+0jRN*c5=F@beD)z!8`~I z`tX|Z=g_z)iannLt<|vJ`}!O$69PHqellf{wXvw+gbUvu$TmUCiz!CL$HyC_Y56*Z zDUN;{IWZAm2M$v9$Z*d36NyHE#)ex^{HhL&FO+6RE9;gjA2(;3uC$Dm=su%#H`X7xD(de)~z&L+r*$dFWddiB~8}-5+ zHeFDU(>L)OH*K~zM-=OLp%%Is@t_xE5+Kt>l9Y@LP_7%hvy8=;hRZ#Nn#rRGHcdgz z85jOhm$|WV7$Ae^QQ``i2P6Y#-HAZZrHp7oAvu!_#&CJT?O%C+Z>1n|e7pS+Q+>2n ztizDG5w{)qWCbi3IB?IM)R9mamiB6XO6^NRS57M>!lNbBM4N$3JkMq0gF|^7@_k+1 z1(rDI3>fueLXf!NRUmATN@i|*va9LUFK%l$i2LS17ru}0@ ze*WYW06taf^&g7Xk8oikF``c<6Na5_Y_vhw+4S^urdqY*wQI+p0oWq|dJ=oiS@f>P z>jY5pY5;UUm2RL{!u%L*I|g(O=*+}P0t#|%6{zWYk)F(FmdE(LA26*BZ)g3uVXzG2 z17#&HX(D$@0=+;F!h*CqV*a`RQ@~^+3q~$&>_4c|F6h0g#3A@E4;f1b^^+A;JV17p z2*|AoNGB|S77leWKeG_Qk8Y$;BrA?4-(%SOo&38%$O-HcG>c#2++Lmq5 zXo;a(KH&@Jt*u?}v({BzK@*Eb&^8RTmBH^GRYWuT*_taU1zaRRkJK$ig|Tx&s>h#WBHd(jGdV{MpHo?>e_Gq-g}h!xwclLX>(>FT91dm*2#JT( zB7;xw*;6A)Oy*)M-p)CcmONm zKX=L6y8jmF;#pWF(28>n2`JKH=iQ0hpzSgrIPX<~a4caow^Vqbow$esa15?%l6mP3 zb}-o>OkSHilLpZaUV%6|TJ03{+_f4g6w(3myb>rq|jH7C1s|Z znB+dSJeC6b-iE-@1$=$M2Jj0OY>jeoWH{8GB#^Ly;S)_wO-MjlZBI!%R#Qt56RWo$ z>p7VvfUDWFo&90}nu-EaL?5)wKW-4HZ~gb+nDjc#svybuv8=fG6u4`dC)lG>^MNM? zgrzcP#;@DE(mM}eG{$Mb2HgS2Etvtm0lK=nEllTdT7N9`NaCl8a?|A z5ReBzKi(;6AMwT4hp$XDhnc5qU$2KUC>KDNSTg8GTqM+&xDYnYQcuk%8iZ}CbP|as zpyic&9`wj=W#|D24jPCM*4EZ!n8EZHKrwE3+x|;sMd#8`%|@E)u3e6;%`yD!;MV8p zg*|&hze&?V*18T9+LEQju*QN8Gf>8iw6&Q@fp(o*)`LkydC<;7n*}`|a%5lyHIT2U zasZWgint2i9WM*-Fr>qrLN zZ9&joxw$gQOv2i26s$~7D~VM2BA2GR!a62fXzFYbqD&2)$s$|5J6`qAY`-;fCq=)-b}-sn$J2BBVBK|#-{Sq4xxRoqYFT>j#xn* z5*4i_2+F$6axxyyhPEm$mbvH{-V>HBf^xHJerRJd}zQzrKG*uqmd@M<7Y!N&&E2uc+v}hwI+}GNthC7t+z-nvnmYKlyhY@R|P|4-TK!@RMMw z55Ge$N`3kEf5)u55~e^@CG7vRj~Eclyg;rK;U7z&XNGQ=7N#Jg?r$TC_WN zjT%JPM+M~t!B+4+;d#6M0xsy^c)Xdu9#eYkT2m|$lP~j$b?YXyMDZr)-8Z|OVGbG+ z63dpoLqjJcpH0VXUW4lL*m~##-|f6!0XYy5uFI3g6z!Cd*m!@x}}(t|=|Oy*jcvzMLL9 zuxeQd?$W14Gid{0-?Mk`?f_5Ue)nvXgv6=8N&(N5kooE_fB)8}#DR z{rjh&<$DfUJPqK6?p_Tc^<8n*`nWnW(#;~Zjh0_NALX>VZh%t#@!ta@FJ_0wmEH6c;8O9?YyUi^ z{^%2mRkGj47&rL-9-tuozM*~b7Ct;pxUgCNjMzY%dcS0+Kg8`H&+(sp>j!%9SED8! z83_sWV6or1=6=cVj&06G@1C3kQGr(be~Sw7c_OPUv#RT%6Yol=6VJjT#~L6evZgD! zM-yGPEThOnu~|K&wi{X% zHNK>7zneFojt&giRo)(Wt>@F7!xKV0UjGOVv6ft!BzHn-x8f|wo7d##igeL+b#?GpWYqD=u?JkPn->hvQA*i{8<}%%SesSxtbh(xOtz)Y2Y+uE z#YLogs;$oRcf0U56}AngmX~vmuR8a`a(#+m$Ge zjY!d^QU!%QMMXAxw(9Quj&FTQ!j%=<7pm@VQ6)D!(iyr5GNvKxy-hL` z!^Q|j{nW1aRhX)DbXKr|0+L6=5YoE5J!49-H`>fqZr@Jctzf8IEa)%##-}H*6svN& zuDDnYQ(s>%TxnH8s>I6d-@pHSoJ^%PM6gyM^xX%t#-nW6k38-ct=aBJ^ly}7gJ#dO zt1%Ai8XiLa+$PB#51s1*#z_XYRP}=gOY6^_o83jOHI7;ZXBbnCEr&DCl&abD(GBtO z@e7A-3UWrOZxMf+nVAV>kHM)8FU>P_chq)`jGSpsRzgLM&-K!b(X$SBy*m-{+QuuhUIB+lpb z_4QS4Ds}K6_&8cxdajhYrQ{k#N7~QQUNdgjLWi~VArOKuxC{;D*k3FTF@u0JH0gsN z9Mh1Gp^zMY0pIb4R;HY(RD!fpnKL0;;5OTxTb$T(eYm~7JvGIIJ141f{^3n& z?%g;ccTvp>Wn!!qD^pxjVpI=&e0xnzjoeXOoG?)}FFRoQ(*x~<9b-`@vK{eGRfF~d zjHs3kuNsD0IXMkyN-9MLOm3`9_Qc9G1@#!S6U5F?erLLf8*HFi6)hkb9>3I8U*EQY zAqX5McPP0J+1muwy?Qkl>2Ga)jr-&JS&p<_n(Q zsQjVkS~J5n)FlFe2%*Zu_=O$OopX$1w8cepaG5`Ek}R;dv#W8nKB=j0g!<4$OT^SX zTAD{{WU60KO+7dNXn|rl@4d#V}@Dgna!>Z)nNy0i6zq6P-K8IJ}>k+g@3QCA&}@*dP+9N5wPmG;5f+Q%^z z+VM-qzz>;owy%%jZV_RaH8dfT!&K4EJj-g9Mt;2FE_4!(*2C;Ge$DAxoIh-(ps&Sf zs=mc=@T2erGrnW-l%XNVsJV~NouncBWf!IBjq!<}Zx@*y`fq zVip&$66ECIK-yTU>M2_kJ=E3HBOTnCh*uKKnL(81urI2rv>7lF3nuDXnU1wJjd12H!K4wb z2?1W4lMhn9K7l{2sr0CIGSeq7d}aWIKmXAUEvK~+U-TGBl;_|PwT@3tPVTcSw#U_o zGk0p+jouDT0zW`Xxx|HS)+#SASF^r8?L&!VS|>+x=lixQ#~wop7shL9P`jxJ295!e z2(dUQC>>lvx%7|e2|Vh6~(sTmoK-eeEjhN`u&{JKcU z>w%J4w&tKPf(zDfL<+v%>Rx>Ks0nJQmS(8of-xaJK~dAH!d5y|j*fW;uGF8sH9yZ+ zF`KDLkZGFfqQY)LUHj87PJ4Z}ACxNWpT*3l>|MDPh|CtpVx<>EDZG@4$k$d$do<@^bG zj0ZBNC+|#sh;2!dgCW*>%}BdtWkaKv-%7H#+cM2HaciadW+_&C=dhr4qgQ9MQixox z@k>w(F7iZ#{QMWN#X*K{#;?Dw{YISMu(39-DX6VwMP9yq*+WYCfMMv;NBS66ZXHdf zP~1V%3W<3Ca9zU&St zj{u+Gb4DmaPmkKAxXYvw67X0Z0tSQm)TW9WJ$cDeuru+7mEFKflQ?<6Sx~n-qjkA2 zt9;N3l-|n&@EHq%UF!?#dbi=V1qx0%Rz_dFW7SgMF3`Q?->citUHWeu%3p&s&H!#=3M>H~j@TWAAd?O+* z^15^O5qFtNiMv3kt~_EqPR>5h5MI`RMx(!rmAPKws2iI!nXGD2SM0-GW4DTiQGyM2 zPj2y|aH*;&x2wxKRM6rPD~YZpJa|yRcT`u%h9KAP&*r%Z5;BhR5|DE)%Z14Ohm2Ov zO7%cIjvtQ=7{5)fOYb~5Tti@U2>gtZeWj(>Or?|`?z3?+g1PImhCJsb5fNMZ_Uz$s z%+vYiUQWaFjc32-aG)%~leQ;oZ)2zi+P;G$<9>cB5jzwbc>Gg5LcsF^MlL)$tg(FA z^!npNJID-9YEii{~}#@mYsoNU6rnxNV4d56tw!iq6b zL>AgHJ*ex#a9Jq7ZK}M-$I<%bsunQ755RkZ4fMu_>gxP}4T=>b=33VUei#@vCgV|> zqRzsll|s}}Kl!;jP)lLA>m6sG9299yIM1>>1|#Qdv_S>xsI{@}PdB{e7c)Qv9i2X+ zwhUX#`c$58HXGVM7=2}Q#GqCUczyb>)iWdmj6a0T4&^fzOTw>(aOOt_s6|@{tj(5e~GY~(Iccx!95x~@FgWB zi%ilg69M|W65tP)FJ1a23q&>Dk6b&pPC^PuB5UAe%>E)9c~sn zZVyl;dYwQ)sB7Tf9KZKZ!R4+xDNKZl|I|o*UPM+_0lYJ-rp!PSZRidr&^pi7 zN!2rTLuI7n-|%L2p+mX%t$WN?WnIacN zf@rjXC{XTxay3Z~A1c?bPd(&U1_h0Wy$7K%n7am7V=<7QwpIm_`cKFnb?4uqNhDUm;9tRvt{W$_su^VshdaGSqmPNF|Bpy= z=(TaqS$B5_khrsyA>5;}i@sPcckE!H)g#8m!XQF0*0l%Jhxj`*S8HQctInmI4kdaN^rKw?@ix_WO#><#~XuIIXbhHPC)z7r>C#=-MLBP?%UBPcY&WbJyzM;Z4JZ;J5Nu~5I;XZ7j@Z=Mm7bJ zG~0tBFhDC*OaaMj6y$sJLgEUA@*+4qJm@X>+S5WViJfZ+(w>eEO1ALI*YiS|KBCR9 z_>-XyK7 z$-S!dhi?%n#lY3x##L6PhEKM_)KXe1pA!lrg?v&19G(Hh4)&*Ea1CqyMe=G89yFpg zO*3xnmTeo7Uc8k=Iycly>okPghpRiC7s~4d?10+DJ_buw_K= zuZL}tX%rm784hQcA2d&2+N!KB7G1)9e9n1@h8AXK+H%1pcd;9Vo)-^x*}Alt2ez-j z8zV%%+U$$X$-xgTkDEORT?$*BdIUw5gODa$95V0Q(KT8(`%&C2qRA`%^*Fh- zj0|{!%+albV0Os;>4z%{3UY=^Xeh(l#A&b={6dYk58kG?PpimxX~^v*hm)K5{(YRs z?We6Z+vIgi3TtXY-Zg|Q+Jh0qSZR-}EK5=U&JC!)zkg*zLqk}${yFLem4p8z@Bke1## zq1e82GVsE~gTGg=Q2FT8O zg;zM4ElD0_I38VOctqT<0ud-@iJ=dPjtn&e6MN6SuhFE3ih{)n8PlPf#R_6qmyO{e z%aJSi>n+Uu*5om}h~-bKRT#A#pd2|kgx~JTOR&yJ&ua&b(e%h;73pn;p->7(=BP=s zZ{KfSUSN3qS_#=$sS_(>!kH>#0#z!CIfJ_#G(EHo3N>QIXm^2}dRWN2N-xdi&W^?I z;fD3h^o50mZczInNMQx=4=8x#tcrEI-jNaKDo_MG9w`{Ipr8>s5MCbk?tc6Wp&uC6 zn|-YEa{Bc>-(&6IW9t(iMn`XSi@V4r)F-IeaT*$M%~y&VJKIus!Cpzif#2kZNC(rV zW?1ilCiCObALsZ@vy%~v`q7bAycU9whpe#H55c{0Xjjis$MT4GotZEPQGpNd$gZzl z6V%Aq`taK%p@5A^-d{twF!6~hkkt;*ESPergIdFeF31vVCBK91|&zLhx& zj?9UF?;~bVFbwp`NuYy&;(qhH(vRWGvVZJW7i;mC^Z${zfS3QR$9MMtL;WQqg9JC+ z@|8a-k3xBOs)NZ~y@Dy01y@ioN27Hq+~nL9{RDcsyu3VQWB9bhW0@qu_^G5Q`d1R4 zW|CSq(^XQa1!7Wn_p2{)yZ@5@|K|ZeW2j&1h+i)cR78e{-F!AJuNxbJDNctO_<@

W;DmR7{v+P|zdpceFn@XH&SvMlpME2PQ^>D}oQy|rcB>En+b%%T z^V)OPFvi_xmsQ{;dROEmBm(?!I9&ZE4W;01+=&+B*3HiTOKa;KgNLU@pW*9OzvHhy z!z%jxJf~IJPYF2IJxAy#lRKEYFTmc}XUux#6yIct$tf|Px5jUkM+v za|STgJrou9-{gG$;;#?B-fU=g`PFH|uO!~+rG5$7?Ooj)Jb4Q^@YOlseljU?-tm<& zXTQ%?Z0CW=F0H+7U%Sa>glK*5Lg2Rbz5cBx{_n&0zjlergT=c_|VK5l|REH$(G-0u3nt@Yp!g&d{YD24n&*;2K6N)9NR27v=6!sAQg#iXa8sK-{+ge6W^o;q7FD;K*&D{ z4g8gd`GEc7%f955|NfSN_&hQSpyxiCeKOED{QF*c4-7pe1;D66MzH0m0T~2{(ij5v zlkDzT5u<+A^s-rN?oohI@xA^RPkGd@G8TGriWroN$wXoX9)I<%Z?k9tyt!`u>GliJx>FGj;GS~VFJYMK3GRU+zR2xbg zsh2hiC@BL%3_V?=r#i>P7|**+PHH7qe7)7)K^maM93TOMS$&N*#=GR}yErsOGki_` zck$}ZZplk-QPo#LQ*=JpK<9Z-M>^FYyz~!rRd;ATu-I*cQfT&2YEcQH-bt-KY(d2t z?hXwZOBDCH=5;Mm5`cY1-niV$8C5XMGvaJarVt(jB#CZZZC9BO<}#TqKC=fs3dXM& z+CB?U3`32gSC@txeB){QL6q0~0Ur(%2_>EN{J7CUFxa(-l?jjJpMHq&M;QLF(|BcJ z!SkG1O1B><amw$i0DitxP!j^x%6>FPe}dPVSP6!*mN4LA1I^baJ&KY21Z`r-vM(T|*G628zM zOE0o%Zfk68l=g^RX<_J|IALp@sWshSV3~bLM^7&W%fRyn1@ow*xyOcdeSM|38%9U6 z;>4m8ki?S}taF^}S5?pio_tu!Y7r zan2oU6L_TT)20-G5xB*`;B|zgGa4V=Q=XOee!>l_Xb31$NjKS!T64R;8kc(RoH7Qj z`R?l=pbI_xuz9V!yStGfAQJ?7#6oMT;LRr0#Rm_%XIX2z3#1R!u`Sfo6D@POr@f-1 z;Z_a~PSl?_sogHL%pTnNPta#ei?xrze}o)HhRzGgx zL-q04V^>f8bAsqlhRV)2^^W{)94rQTfcwEdZJWdcby)lT-MMW3e)MKoBu4vsdLM}!?gkfI3F60kiZ36skn{Cg7Jt8B6 zv?CUe6uSe}Iwvz99Cbk`mY5%*<@~6iuP2TBhj{h8^FYM2InB&mt8I zqWOVgTp!5R9sviHQ160?Z?-9jT}nanai57JVPtl`*Gx%p_1*n%X&KSy!Jfiph+~(4 zbQ7Qq9Baha9PE%4+^8DPsjRHjmNwb=^nPWs6}4~42oy(>(-bTaB8EEu!1c7lR}=Pt z1rnqfJ39j=9VT8;09x8V2a+d%pqm9CQ7F{P%y+S_OdJmnKzf@eQcFK{n7@b9BbV0R z*u{SA@3$4_Lj>62=;_YUXI6nHY1~V`WHtyw? zUSu16NK-SsEA&;V@rk96&#^tm+_`+;EnBx5LN-3d@M>XH{Bj;((#wy{6vNekD#S|x z3sSP`VWkCi86c^KRWd+|}NL>ye@ z%+Djx?i{@YHX4l9zq3{(f8|VRwyq#F2#_qN$|;ZYGgE8Z!%%{&>n8w7e+9<6Awn(~ zr9tX?`K;(xgqeE!_9R)sowHU}2DP58o`ozLR%GEehA*ylBRHzd&vdzHH@@_`(+b9H zNd+@^=aDQD+2tu7OoRuFLC&AAcWDe;sdX8nbWp-or&LqNA;Mu)Tq{V6%n<3$tUWW( z;hnNaUF!tSs6vuEf`&K8AORWq#-m^+!#!f&#VkcJH-Ld-<3(#kYjk&47YzVn2Zw{H zZ6AbJ!S)U2%#`S~5s+VVHSs5_j>gIWn>aRZ$f-yxx(3z)`tJiMjchGS^RSacuEn8U zK#R#N%!{~f>E}n|0A&!Uh9^Uph5)!THs@h5L9_XV3@oG2bEebD1}y!&y(Wgw!`rznhMsbTBXHR(Z!2fD3b}^6tt9IFQm1Fy5pB zhN}1Y5g^jz_Nb!d*aq;aw@>TSzH`-Kd)=qi~z)X-2&x zNJzRtGY#Va6;}Yo-2_0mYuM0Ig8@P$h(0Uh)ejyi@;VDwd#~bpHVp)=%{8*Xwvqkd z08P^*BG|uGynO=}YL*grX&acOP&`~6hw{Qi0;*x4Fwzeus?c;{HNEnsfn^%D+Ujb< zBo+?2<7SLYJgZgsdLeB5`58FM5K((n|Kq=S%-eC9qpTAi#kin5-0qivBFg(Qa@Jb( zC76J%2a>Ri_?HZvTvJ2HqxoQi=$am)7~Q&Y^TL@kG*{qo&vaB%+s=Z1c;drjl~e{n zB*gGCNP~lAA}oQ2rIK+7xPU-;1VAzMr^ixr4)SoIVDA#KFlRP+1kFrE)0@)cdbpm? zv}v!HHi26E2mVDY6jfH1ECsuN|F{#Z0XZ(|arD4Wi;~essdkljsRCtC_rL6)OjU&)5m7~mbsO-Kq^el<$WQV1rMgKimCBFj9y!>am~Op3 zKg8!0n{}cH+)CPR0DgDalCQ3>t?#+fomaY8R%(25NxX+8-r~U2Cewg&Uj*+Y+wfO_&a9r6mqyNZw_348dyoABG+jrKSGI3AA2AUG4|8Np_s(%Qx{kYf zAjCizD1}i~WJAW2H+iY4scqg>6>{!j*QV=(W?%ET+${h}hPIpottykl*}Jsg4=gh0 zb$L%NlF%OY(E@GlH(O2v-Mxe=R(8 z*u18+)U!bk8pf(hPHqPhtjonhO9Ww|eI1p zLRyizhy0TM63NEehPajtcEjw%J11&KvbMmN!uU6&cQc!_w^Au)?sypoY5sNjq@$&M>nJcNT)xcdYu~@&8S{)i zawzLyc*bvl+59~|5;^}JJvyy=2CE1(dHp0=oU4svm`U%)@+mQA zwN{S#bY~BU{Tax=4pPcub}|hVWOH+CB*h$+n%Xc^u{4HV?KEWYmp5J_#htf{OKS*@ z*)5#{AWGEpE<)#epo)J8LhrJtdA6*Lgtt9|3yVN3=Xam=oNN+rba}PEU+wOG zfEQK_bakZ~0!L5F0n#)XELrIJjY#eRYrzdj&ZTvh?8}ah?5#7M;c9FW0ndPf7%D2_ zRG&;c@)Ix`kl?u2n-i1{kBIkT^(qB#!NML?8>Cmge;aVkA8uE7Tpn2ly`;adulzvJ zJOW*a)6UFU0%fz|{3C;tOB*Z0DE5#p*bC0lr3vsLNcIJa5yvo8-6EUyR>#MS0ap4v zhVxNv@2cj5t!k0)*VM>}@A;2wesQVX&|C)ilSgn?4e1DvL6LA(Ue1+rAILDU%t z-OkoF7))X_L@=gy_jGkxf{o#oDu8e=e-`+`(jMOE`n&sNZry1xeLczUi=p&sz4R6D zCe6;mf0*e(;=JG98&FwgV(;j}gzn-60N9TQM(>MZZl19;>Zpcs8UgHz)c^vy7ql*o zOl8-|1Ek+BWOuZ;J8v-&PY+VcU}L~-*3jSxccu%GKhs_@J|FDQ%1^;lb1v@r0)r7! z`ovTf^xIT}+x@~P0N)?5vk%omf}LtBR*J8p{_540``>OqCm{1TCKGycjP#3w-amvF z(;F6B9)qpZ957xqlpXIPsQ|K;q%-Rys~KI-qGLsjSHMGZG+gjOV9bvD8psKfl*n2sLz$Y2+1C?Gx)_wIFTx)zgMdR8Wb zMl^-Syvk*aADO7Cof#G?HkNmHB(|ZZM(EMgVRvXt-42IZ_El2rrLhH88348*;##oc zEoOJnV_~F~OpF2-`il@}q`($&{w9;_{@(!x)5%+3Uq{ga{eQbvvvIjoDB$@Vb*{dd z6tDw`cw^6SdF;_tzrF1{bfHp?vEWFH;R>^<)2y)Hl@1HRr4FHmZ1wB16w+sl>-^FI1xNrcxckIOP|9Ihz2BBqa8%_Mm z$uG#V;PwAMAIl<)q$KDFB_!_gOC9z;`~1iMKflVKSN*32{G5YHE=j<@&60(-*(tqiLZwqEPea)2e1GtA#vfH)!8!BtM~p7MUfgL literal 16572 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 C%{D;* diff --git a/testing/resources/performance_overlay_gold_90fps.png b/testing/resources/performance_overlay_gold_90fps.png index 150123ca046fdfc477dd321f3f52b4571a640b23..962e2875d4fde53aa45a76081099ee76bc5a7330 100644 GIT binary patch literal 16293 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

Since View.setSystemGestureExclusionRects receives a JSONArray containing JSONObjects, these - * values need to be transformed into the expected input of View.setSystemGestureExclusionRects, - * which is ArrayList. - * - *

This method is used by the SystemGestures.setSystemGestureExclusionRects platform channel. - * - * @throws JSONException if {@code inputRects} does not contain expected keys and value types. - */ - @NonNull - private ArrayList decodeExclusionRects(@NonNull JSONArray inputRects) throws JSONException { - ArrayList exclusionRects = new ArrayList(); - for (int i = 0; i < inputRects.length(); i++) { - JSONObject rect = inputRects.getJSONObject(i); - int top; - int right; - int bottom; - int left; - - try { - top = rect.getInt("top"); - right = rect.getInt("right"); - bottom = rect.getInt("bottom"); - left = rect.getInt("left"); - } catch (JSONException exception) { - throw new JSONException( - "Incorrect JSON data shape. To set system gesture exclusion rects, \n" - + "a JSONObject with top, right, bottom and left values need to be set to int values."); - } - - Rect gestureRect = new Rect(left, top, right, bottom); - exclusionRects.add(gestureRect); - } - - return exclusionRects; - } - - /** - * Encodes a List provided by the Android host into an ArrayList>. - * - *

Since View.getSystemGestureExclusionRects returns a list of Rects, these Rects need to be - * transformed into UTF-8 encoded JSON messages to be properly decoded by the Flutter framework. - * - *

This method is used by the SystemGestures.getSystemGestureExclusionRects platform channel. - */ - private ArrayList> encodeExclusionRects(List exclusionRects) { - ArrayList> encodedExclusionRects = - new ArrayList>(); - for (Rect rect : exclusionRects) { - HashMap rectMap = new HashMap(); - rectMap.put("top", rect.top); - rectMap.put("right", rect.right); - rectMap.put("bottom", rect.bottom); - rectMap.put("left", rect.left); - encodedExclusionRects.add(rectMap); - } - - return encodedExclusionRects; - } - @NonNull private AppSwitcherDescription decodeAppSwitcherDescription( @NonNull JSONObject encodedDescription) throws JSONException { @@ -515,15 +426,6 @@ public interface PlatformMessageHandler { * {@code text}. */ void setClipboardData(@NonNull String text); - - /** The Flutter application would like to get the system gesture exclusion rects. */ - List getSystemGestureExclusionRects(); - - /** - * The Flutter application would like to set the system gesture exclusion rects through the - * given {@code rects}. - */ - void setSystemGestureExclusionRects(@NonNull ArrayList rects); } /** Types of sounds the Android OS can play on behalf of an application. */ diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 2c88dce423938..38f2b275fd275 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -9,7 +9,6 @@ import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.graphics.Rect; import android.os.Build; import android.view.HapticFeedbackConstants; import android.view.SoundEffectConstants; @@ -19,7 +18,6 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import io.flutter.embedding.engine.systemchannels.PlatformChannel; -import java.util.ArrayList; import java.util.List; /** Android implementation of the platform plugin. */ @@ -87,16 +85,6 @@ public CharSequence getClipboardData( public void setClipboardData(@NonNull String text) { PlatformPlugin.this.setClipboardData(text); } - - @Override - public List getSystemGestureExclusionRects() { - return PlatformPlugin.this.getSystemGestureExclusionRects(); - } - - @Override - public void setSystemGestureExclusionRects(@NonNull ArrayList rects) { - PlatformPlugin.this.setSystemGestureExclusionRects(rects); - } }; public PlatformPlugin(Activity activity, PlatformChannel platformChannel) { @@ -299,24 +287,4 @@ private void setClipboardData(String text) { ClipData clip = ClipData.newPlainText("text label?", text); clipboard.setPrimaryClip(clip); } - - private List getSystemGestureExclusionRects() { - if (Build.VERSION.SDK_INT >= 29) { - Window window = activity.getWindow(); - View view = window.getDecorView(); - return view.getSystemGestureExclusionRects(); - } - - return null; - } - - private void setSystemGestureExclusionRects(ArrayList rects) { - if (Build.VERSION.SDK_INT < 29) { - return; - } - - Window window = activity.getWindow(); - View view = window.getDecorView(); - view.setSystemGestureExclusionRects(rects); - } } diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index daf060a2c3b9f..d793f5a731877 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -15,7 +15,6 @@ import io.flutter.embedding.engine.RenderingComponentTest; import io.flutter.embedding.engine.plugins.shim.ShimPluginRegistryTest; import io.flutter.embedding.engine.renderer.FlutterRendererTest; -import io.flutter.embedding.engine.systemchannels.PlatformChannelTest; import io.flutter.external.FlutterLaunchTests; import io.flutter.plugin.common.StandardMessageCodecTest; import io.flutter.plugin.editing.InputConnectionAdaptorTest; @@ -47,7 +46,6 @@ FlutterRendererTest.class, FlutterViewTest.class, InputConnectionAdaptorTest.class, - PlatformChannelTest.class, PlatformPluginTest.class, PluginComponentTest.class, PreconditionsTest.class, diff --git a/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java b/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java deleted file mode 100644 index ed34a112f8fad..0000000000000 --- a/shell/platform/android/test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java +++ /dev/null @@ -1,203 +0,0 @@ -package io.flutter.embedding.engine.systemchannels; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.graphics.Rect; -import io.flutter.embedding.engine.dart.DartExecutor; -import io.flutter.embedding.engine.systemchannels.PlatformChannel.PlatformMessageHandler; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel.Result; -import java.util.ArrayList; -import java.util.HashMap; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; - -@Config(manifest = Config.NONE) -@RunWith(RobolectricTestRunner.class) -public class PlatformChannelTest { - @Test - public void itSendsSuccessMessageToFrameworkWhenGettingSystemGestureExclusionRects() - throws JSONException { - // --- Test Setup --- - DartExecutor dartExecutor = mock(DartExecutor.class); - PlatformChannel platformChannel = new PlatformChannel(dartExecutor); - PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class); - platformChannel.setPlatformMessageHandler(platformMessageHandler); - Result result = mock(Result.class); - - // Fake API output setup - ArrayList fakeExclusionRects = new ArrayList(); - Rect gestureRect = new Rect(0, 0, 500, 250); - fakeExclusionRects.add(gestureRect); - when(platformMessageHandler.getSystemGestureExclusionRects()).thenReturn(fakeExclusionRects); - - // Parsed API output that should be passed to result.success() - ArrayList> expectedEncodedOutputRects = - new ArrayList>(); - HashMap rectMap = new HashMap(); - rectMap.put("top", 0); - rectMap.put("right", 500); - rectMap.put("bottom", 250); - rectMap.put("left", 0); - expectedEncodedOutputRects.add(rectMap); - MethodCall callGetSystemGestureExclusionRects = - new MethodCall("SystemGestures.getSystemGestureExclusionRects", null); - - // --- Execute Test --- - platformChannel.parsingMethodCallHandler.onMethodCall( - callGetSystemGestureExclusionRects, result); - - // --- Verify Results --- - verify(result, times(1)).success(expectedEncodedOutputRects); - } - - @Test - public void - itSendsAPILevelErrorWhenAndroidVersionIsTooLowWhenGettingSystemGestureExclusionRects() { - // --- Test Setup --- - DartExecutor dartExecutor = mock(DartExecutor.class); - PlatformChannel platformChannel = new PlatformChannel(dartExecutor); - PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class); - platformChannel.setPlatformMessageHandler(platformMessageHandler); - when(platformMessageHandler.getSystemGestureExclusionRects()).thenReturn(null); - Result result = mock(Result.class); - - MethodCall callGetSystemGestureExclusionRects = - new MethodCall("SystemGestures.getSystemGestureExclusionRects", null); - - // --- Execute Test --- - platformChannel.parsingMethodCallHandler.onMethodCall( - callGetSystemGestureExclusionRects, result); - - // --- Verify Results --- - verify(result, times(1)) - .error("error", "Exclusion rects only exist for Android API 29+.", null); - } - - @Test - public void itSendsSuccessMessageToFrameworkWhenSettingSystemGestureExclusionRects() - throws JSONException { - // --- Test Setup --- - DartExecutor dartExecutor = mock(DartExecutor.class); - PlatformChannel platformChannel = new PlatformChannel(dartExecutor); - PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class); - platformChannel.setPlatformMessageHandler(platformMessageHandler); - Result result = mock(Result.class); - - JSONObject jsonRect = new JSONObject(); - jsonRect.put("top", 0); - jsonRect.put("right", 500); - jsonRect.put("bottom", 250); - jsonRect.put("left", 0); - JSONArray jsonExclusionRectsFromPlatform = new JSONArray(); - jsonExclusionRectsFromPlatform.put(jsonRect); - - MethodCall callSystemGestureExclusionRects = - new MethodCall( - "SystemGestures.setSystemGestureExclusionRects", jsonExclusionRectsFromPlatform); - - // --- Execute Test --- - platformChannel.parsingMethodCallHandler.onMethodCall(callSystemGestureExclusionRects, result); - - // --- Verify Results --- - verify(result, times(1)).success(null); - } - - @Test - public void itProperlyDecodesGestureRectsWhenSettingSystemGestureExclusionRects() - throws JSONException { - // --- Test Setup --- - DartExecutor dartExecutor = mock(DartExecutor.class); - PlatformChannel platformChannel = new PlatformChannel(dartExecutor); - PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class); - platformChannel.setPlatformMessageHandler(platformMessageHandler); - Result result = mock(Result.class); - - JSONObject jsonRect = new JSONObject(); - jsonRect.put("top", 0); - jsonRect.put("right", 500); - jsonRect.put("bottom", 250); - jsonRect.put("left", 0); - JSONArray jsonExclusionRectsFromPlatform = new JSONArray(); - jsonExclusionRectsFromPlatform.put(jsonRect); - - ArrayList expectedDecodedRects = new ArrayList(); - Rect gestureRect = new Rect(0, 0, 500, 250); - expectedDecodedRects.add(gestureRect); - - MethodCall callSetSystemGestureExclusionRects = - new MethodCall( - "SystemGestures.setSystemGestureExclusionRects", jsonExclusionRectsFromPlatform); - - // --- Execute Test --- - platformChannel.parsingMethodCallHandler.onMethodCall( - callSetSystemGestureExclusionRects, result); - - // --- Verify Results --- - verify(platformMessageHandler, times(1)).setSystemGestureExclusionRects(expectedDecodedRects); - } - - @Test - public void itSendsJSONInputErrorWhenNonJSONInputIsUsedWhenSettingSystemGestureExclusionRects() { - // --- Test Setup --- - DartExecutor dartExecutor = mock(DartExecutor.class); - PlatformChannel platformChannel = new PlatformChannel(dartExecutor); - PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class); - platformChannel.setPlatformMessageHandler(platformMessageHandler); - Result result = mock(Result.class); - - String nonJsonInput = "Non-JSON"; - MethodCall callSetSystemGestureExclusionRects = - new MethodCall("SystemGestures.setSystemGestureExclusionRects", nonJsonInput); - - // --- Execute Test --- - platformChannel.parsingMethodCallHandler.onMethodCall( - callSetSystemGestureExclusionRects, result); - - // --- Verify Results --- - String inputTypeError = - "Input type is incorrect. Ensure that a List> is passed as the input for SystemGestureExclusionRects.setSystemGestureExclusionRects."; - verify(result, times(1)).error("inputTypeError", inputTypeError, null); - } - - @Test - public void itSendsJSONErrorWhenIncorrectJSONShapeIsUsedWhenSettingSystemGestureExclusionRects() - throws JSONException { - // --- Test Setup --- - DartExecutor dartExecutor = mock(DartExecutor.class); - PlatformChannel platformChannel = new PlatformChannel(dartExecutor); - PlatformMessageHandler platformMessageHandler = mock(PlatformMessageHandler.class); - platformChannel.setPlatformMessageHandler(platformMessageHandler); - Result result = mock(Result.class); - - // Add key/value pairs that aren't needed by exclusion rects to simulate incorrect JSON shape - JSONObject jsonObject = new JSONObject(); - jsonObject.put("arg1", 0); - jsonObject.put("arg2", 500); - JSONArray inputArray = new JSONArray(); - inputArray.put(jsonObject); - - MethodCall callSetSystemGestureExclusionRects = - new MethodCall("SystemGestures.setSystemGestureExclusionRects", inputArray); - - // --- Execute Test --- - platformChannel.parsingMethodCallHandler.onMethodCall( - callSetSystemGestureExclusionRects, result); - - // --- Verify Results --- - verify(result, times(1)) - .error( - "error", - "JSON error: Incorrect JSON data shape. To set system gesture exclusion rects, \n" - + "a JSONObject with top, right, bottom and left values need to be set to int values.", - null); - } -} From 16272b0df3196275c6a8250aa16053394abf9c66 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 9 Apr 2020 23:45:04 -0400 Subject: [PATCH 336/386] Roll src/third_party/skia 8561fc23c927..854ac61e90a4 (36 commits) (#17620) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7a9aab262d249..761389e3649e0 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '8561fc23c927525d370b62b73b431d51ed574935', + 'skia_revision': '854ac61e90a49a55146ed85ab364804320ca3476', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index e390b1a7cd837..94bc7d3739db8 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: cf555339cd3f5572c9e165ed69278b0c +Signature: 22874acfdd6dbdcd7b49b1089739ad65 UNUSED LICENSES: From 262723a254b5b1c041f2a4dc5cbc1da5b46ee743 Mon Sep 17 00:00:00 2001 From: Francisco Magdaleno Date: Thu, 9 Apr 2020 20:55:02 -0700 Subject: [PATCH 337/386] [windows] Sends complete key data to framework (#17577) --- .../cpp/client_wrapper/standard_codec.cc | 1 - shell/platform/windows/key_event_handler.cc | 70 ++++++++++++++++++- shell/platform/windows/key_event_handler.h | 2 +- .../platform/windows/keyboard_hook_handler.h | 2 +- .../windows/testing/win32_window_test.cc | 5 +- .../windows/testing/win32_window_test.h | 2 +- shell/platform/windows/text_input_plugin.cc | 2 +- shell/platform/windows/text_input_plugin.h | 2 +- .../platform/windows/win32_flutter_window.cc | 14 ++-- shell/platform/windows/win32_flutter_window.h | 4 +- shell/platform/windows/win32_window.cc | 65 +++++++++++++---- shell/platform/windows/win32_window.h | 6 +- 12 files changed, 144 insertions(+), 31 deletions(-) diff --git a/shell/platform/common/cpp/client_wrapper/standard_codec.cc b/shell/platform/common/cpp/client_wrapper/standard_codec.cc index 137a3e849002c..d16ce3b54f077 100644 --- a/shell/platform/common/cpp/client_wrapper/standard_codec.cc +++ b/shell/platform/common/cpp/client_wrapper/standard_codec.cc @@ -83,7 +83,6 @@ StandardCodecSerializer::~StandardCodecSerializer() = default; EncodableValue StandardCodecSerializer::ReadValue( ByteBufferStreamReader* stream) const { EncodedType type = static_cast(stream->ReadByte()); - ; switch (type) { case EncodedType::kNull: return EncodableValue(); diff --git a/shell/platform/windows/key_event_handler.cc b/shell/platform/windows/key_event_handler.cc index f611210174b75..8079f197cf333 100644 --- a/shell/platform/windows/key_event_handler.cc +++ b/shell/platform/windows/key_event_handler.cc @@ -13,15 +13,76 @@ static constexpr char kChannelName[] = "flutter/keyevent"; static constexpr char kKeyCodeKey[] = "keyCode"; +static constexpr char kScanCodeKey[] = "scanCode"; +static constexpr char kCharacterCodePointKey[] = "characterCodePoint"; +static constexpr char kModifiersKey[] = "modifiers"; static constexpr char kKeyMapKey[] = "keymap"; static constexpr char kTypeKey[] = "type"; -static constexpr char kAndroidKeyMap[] = "android"; +static constexpr char kWindowsKeyMap[] = "windows"; static constexpr char kKeyUp[] = "keyup"; static constexpr char kKeyDown[] = "keydown"; namespace flutter { +// Re-definition of the modifiers for compatibility with the Flutter framework. +// These have to be in sync with the framework's RawKeyEventDataWindows +// modifiers definition. +// https://github.com/flutter/flutter/blob/19ff596979e407c484a32f4071420fca4f4c885f/packages/flutter/lib/src/services/raw_keyboard_windows.dart#L203 +const int kShift = 1 << 0; +const int kShiftLeft = 1 << 1; +const int kShiftRight = 1 << 2; +const int kControl = 1 << 3; +const int kControlLeft = 1 << 4; +const int kControlRight = 1 << 5; +const int kAlt = 1 << 6; +const int kAltLeft = 1 << 7; +const int kAltRight = 1 << 8; +const int kWinLeft = 1 << 9; +const int kWinRight = 1 << 10; +const int kCapsLock = 1 << 11; +const int kNumLock = 1 << 12; +const int kScrollLock = 1 << 13; + +namespace { +/// Calls GetKeyState() an all modifier keys and packs the result in an int, +/// with the re-defined values declared above for compatibility with the Flutter +/// framework. +int GetModsForKeyState() { + int mods = 0; + + if (GetKeyState(VK_SHIFT) < 0) + mods |= kShift; + if (GetKeyState(VK_LSHIFT) < 0) + mods |= kShiftLeft; + if (GetKeyState(VK_RSHIFT) < 0) + mods |= kShiftRight; + if (GetKeyState(VK_CONTROL) < 0) + mods |= kControl; + if (GetKeyState(VK_LCONTROL) < 0) + mods |= kControlLeft; + if (GetKeyState(VK_RCONTROL) < 0) + mods |= kControlRight; + if (GetKeyState(VK_MENU) < 0) + mods |= kAlt; + if (GetKeyState(VK_LMENU) < 0) + mods |= kAltLeft; + if (GetKeyState(VK_RMENU) < 0) + mods |= kAltRight; + if (GetKeyState(VK_LWIN) < 0) + mods |= kWinLeft; + if (GetKeyState(VK_RWIN) < 0) + mods |= kWinRight; + if (GetKeyState(VK_CAPITAL) < 0) + mods |= kCapsLock; + if (GetKeyState(VK_NUMLOCK) < 0) + mods |= kNumLock; + if (GetKeyState(VK_SCROLL) < 0) + mods |= kScrollLock; + return mods; +} +} // namespace + KeyEventHandler::KeyEventHandler(flutter::BinaryMessenger* messenger) : channel_( std::make_unique>( @@ -38,13 +99,16 @@ void KeyEventHandler::KeyboardHook(Win32FlutterWindow* window, int key, int scancode, int action, - int mods) { + char32_t character) { // TODO: Translate to a cross-platform key code system rather than passing // the native key code. rapidjson::Document event(rapidjson::kObjectType); auto& allocator = event.GetAllocator(); event.AddMember(kKeyCodeKey, key, allocator); - event.AddMember(kKeyMapKey, kAndroidKeyMap, allocator); + event.AddMember(kScanCodeKey, scancode, allocator); + event.AddMember(kCharacterCodePointKey, character, allocator); + event.AddMember(kKeyMapKey, kWindowsKeyMap, allocator); + event.AddMember(kModifiersKey, GetModsForKeyState(), allocator); switch (action) { case WM_KEYDOWN: diff --git a/shell/platform/windows/key_event_handler.h b/shell/platform/windows/key_event_handler.h index 47ec63f6d1755..1b28e90b71787 100644 --- a/shell/platform/windows/key_event_handler.h +++ b/shell/platform/windows/key_event_handler.h @@ -31,7 +31,7 @@ class KeyEventHandler : public KeyboardHookHandler { int key, int scancode, int action, - int mods) override; + char32_t character) override; // |KeyboardHookHandler| void CharHook(Win32FlutterWindow* window, char32_t code_point) override; diff --git a/shell/platform/windows/keyboard_hook_handler.h b/shell/platform/windows/keyboard_hook_handler.h index 1b3304ddd50b0..4583b2bb76d3b 100644 --- a/shell/platform/windows/keyboard_hook_handler.h +++ b/shell/platform/windows/keyboard_hook_handler.h @@ -21,7 +21,7 @@ class KeyboardHookHandler { int key, int scancode, int action, - int mods) = 0; + char32_t character) = 0; // A function for hooking into unicode code point input. virtual void CharHook(Win32FlutterWindow* window, char32_t code_point) = 0; diff --git a/shell/platform/windows/testing/win32_window_test.cc b/shell/platform/windows/testing/win32_window_test.cc index 13a70e039647c..53ad12b5b71d9 100644 --- a/shell/platform/windows/testing/win32_window_test.cc +++ b/shell/platform/windows/testing/win32_window_test.cc @@ -21,7 +21,10 @@ void Win32WindowTest::OnPointerLeave() {} void Win32WindowTest::OnChar(char32_t code_point) {} -void Win32WindowTest::OnKey(int key, int scancode, int action, int mods) {} +void Win32WindowTest::OnKey(int key, + int scancode, + int action, + char32_t character) {} void Win32WindowTest::OnScroll(double delta_x, double delta_y) {} diff --git a/shell/platform/windows/testing/win32_window_test.h b/shell/platform/windows/testing/win32_window_test.h index ccefdfe717dbf..77d25464b9e9a 100644 --- a/shell/platform/windows/testing/win32_window_test.h +++ b/shell/platform/windows/testing/win32_window_test.h @@ -45,7 +45,7 @@ class Win32WindowTest : public Win32Window { void OnChar(char32_t code_point) override; // |Win32Window| - void OnKey(int key, int scancode, int action, int mods) override; + void OnKey(int key, int scancode, int action, char32_t character) override; // |Win32Window| void OnScroll(double delta_x, double delta_y) override; diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index 0fd4dee9d2442..10b3864d4fcdf 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -49,7 +49,7 @@ void TextInputPlugin::KeyboardHook(Win32FlutterWindow* window, int key, int scancode, int action, - int mods) { + char32_t character) { if (active_model_ == nullptr) { return; } diff --git a/shell/platform/windows/text_input_plugin.h b/shell/platform/windows/text_input_plugin.h index 03cf9b7c5547b..dced9a69545eb 100644 --- a/shell/platform/windows/text_input_plugin.h +++ b/shell/platform/windows/text_input_plugin.h @@ -32,7 +32,7 @@ class TextInputPlugin : public KeyboardHookHandler { int key, int scancode, int action, - int mods) override; + char32_t character) override; // |KeyboardHookHandler| void CharHook(Win32FlutterWindow* window, char32_t code_point) override; diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index cf69618835373..73c46febf3f7b 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -167,9 +167,12 @@ void Win32FlutterWindow::OnChar(char32_t code_point) { } } -void Win32FlutterWindow::OnKey(int key, int scancode, int action, int mods) { +void Win32FlutterWindow::OnKey(int key, + int scancode, + int action, + char32_t character) { if (process_events_) { - SendKey(key, scancode, action, 0); + SendKey(key, scancode, action, character); } } @@ -267,9 +270,12 @@ void Win32FlutterWindow::SendChar(char32_t code_point) { } } -void Win32FlutterWindow::SendKey(int key, int scancode, int action, int mods) { +void Win32FlutterWindow::SendKey(int key, + int scancode, + int action, + char32_t character) { for (const auto& handler : keyboard_hook_handlers_) { - handler->KeyboardHook(this, key, scancode, action, mods); + handler->KeyboardHook(this, key, scancode, action, character); } } diff --git a/shell/platform/windows/win32_flutter_window.h b/shell/platform/windows/win32_flutter_window.h index 5fa58aa295c8c..bce1f2e6fd644 100644 --- a/shell/platform/windows/win32_flutter_window.h +++ b/shell/platform/windows/win32_flutter_window.h @@ -60,7 +60,7 @@ class Win32FlutterWindow : public Win32Window { void OnChar(char32_t code_point) override; // |Win32Window| - void OnKey(int key, int scancode, int action, int mods) override; + void OnKey(int key, int scancode, int action, char32_t character) override; // |Win32Window| void OnScroll(double delta_x, double delta_y) override; @@ -116,7 +116,7 @@ class Win32FlutterWindow : public Win32Window { void SendChar(char32_t code_point); // Reports a raw keyboard message to Flutter engine. - void SendKey(int key, int scancode, int action, int mods); + void SendKey(int key, int scancode, int action, char32_t character); // Reports scroll wheel events to Flutter engine. void SendScroll(double delta_x, double delta_y); diff --git a/shell/platform/windows/win32_window.cc b/shell/platform/windows/win32_window.cc index 0abafb562fbf1..6f8c48e9c03d6 100644 --- a/shell/platform/windows/win32_window.cc +++ b/shell/platform/windows/win32_window.cc @@ -110,6 +110,7 @@ Win32Window::MessageHandler(HWND hwnd, auto window = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); UINT button_pressed = 0; + if (window != nullptr) { switch (message) { case kWmDpiChangedBeforeParent: @@ -190,35 +191,71 @@ Win32Window::MessageHandler(HWND hwnd, // DefWindowProc will send WM_CHAR for this WM_UNICHAR. break; } + case WM_DEADCHAR: + case WM_SYSDEADCHAR: case WM_CHAR: case WM_SYSCHAR: { - if (wparam == VK_BACK) - break; char32_t code_point = static_cast(wparam); static char32_t lead_surrogate = 0; - // If code_point is LeadSurrogate, save and return. + // If code_point is LeadSurrogate, save to combine to potentially form + // a complex Unicode character. if ((code_point & 0xFFFFFC00) == 0xD800) { lead_surrogate = code_point; - return TRUE; - } - // Merge TrailSurrogate and LeadSurrogate. - if (lead_surrogate != 0 && (code_point & 0xFFFFFC00) == 0xDC00) { + } else if (lead_surrogate != 0 && (code_point & 0xFFFFFC00) == 0xDC00) { + // Merge TrailSurrogate and LeadSurrogate. code_point = 0x10000 + ((lead_surrogate & 0x000003FF) << 10) + (code_point & 0x3FF); + lead_surrogate = 0; + } + + // In an ENG-INTL keyboard, pressing "'" + "e" produces é. In this case, + // the "'" key is a dead char, and shouldn't be sent to window->OnChar + // for text input. However, the key event should still be sent to + // Flutter. The result would be: + // * Key event - key code: 222 (quote) - key label: ' + // * Key event - key code: 69 (e) - key label: é + // + // As for text input, only the second key press will display a + // character. + if (wparam != VK_BACK && message != WM_DEADCHAR && + message != WM_SYSDEADCHAR) { + window->OnChar(code_point); + } + + // All key presses that generate a character should be sent from + // WM_CHAR. In order to send the full key press information, the keycode + // is persisted in keycode_for_char_message_ obtained from WM_KEYDOWN. + if (keycode_for_char_message_ != 0) { + const unsigned int scancode = (lparam >> 16) & 0xff; + window->OnKey(keycode_for_char_message_, scancode, WM_KEYDOWN, + code_point); + keycode_for_char_message_ = 0; } - lead_surrogate = 0; - window->OnChar(code_point); break; } case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: - unsigned char scancode = ((unsigned char*)&lparam)[2]; - unsigned int virtualKey = MapVirtualKey(scancode, MAPVK_VSC_TO_VK); - const int key = virtualKey; - const int action = message == WM_KEYDOWN ? WM_KEYDOWN : WM_KEYUP; - window->OnKey(key, scancode, action, 0); + const bool is_keydown_message = + (message == WM_KEYDOWN || message == WM_SYSKEYDOWN); + // Check if this key produces a character. If so, the key press should + // be sent with the character produced at WM_CHAR. Store the produced + // keycode (it's not accessible from WM_CHAR) to be used in WM_CHAR. + const unsigned int character = MapVirtualKey(wparam, MAPVK_VK_TO_CHAR); + if (character > 0 && is_keydown_message) { + keycode_for_char_message_ = wparam; + break; + } + unsigned int keyCode(wparam); + const unsigned int scancode = (lparam >> 16) & 0xff; + // If the key is a modifier, get its side. + if (keyCode == VK_SHIFT || keyCode == VK_MENU || + keyCode == VK_CONTROL) { + keyCode = MapVirtualKey(scancode, MAPVK_VSC_TO_VK_EX); + } + const int action = is_keydown_message ? WM_KEYDOWN : WM_KEYUP; + window->OnKey(keyCode, scancode, action, 0); break; } return DefWindowProc(hwnd, message, wparam, lparam); diff --git a/shell/platform/windows/win32_window.h b/shell/platform/windows/win32_window.h index dfb5928048168..ba959ffcaa3b9 100644 --- a/shell/platform/windows/win32_window.h +++ b/shell/platform/windows/win32_window.h @@ -106,7 +106,7 @@ class Win32Window { virtual void OnChar(char32_t code_point) = 0; // Called when raw keyboard input occurs. - virtual void OnKey(int key, int scancode, int action, int mods) = 0; + virtual void OnKey(int key, int scancode, int action, char32_t character) = 0; // Called when mouse scrollwheel input occurs. virtual void OnScroll(double delta_x, double delta_y) = 0; @@ -172,6 +172,10 @@ class Win32Window { // Keeps track of mouse state in relation to the window. MouseState mouse_state_; + + // Keeps track of the last key code produced by a WM_KEYDOWN or WM_SYSKEYDOWN + // message. + int keycode_for_char_message_ = 0; }; } // namespace flutter From 6b065297cf774b97404d20908616b08cdcf21824 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Thu, 9 Apr 2020 22:18:42 -0700 Subject: [PATCH 338/386] Fix Path hit test code for high dpi devices (#17608) --- lib/web_ui/lib/src/engine/surface/painting.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/surface/painting.dart b/lib/web_ui/lib/src/engine/surface/painting.dart index def459d055032..59388db32493b 100644 --- a/lib/web_ui/lib/src/engine/surface/painting.dart +++ b/lib/web_ui/lib/src/engine/surface/painting.dart @@ -934,7 +934,9 @@ class SurfacePath implements ui.Path { EngineWindow.browserDevicePixelRatio) { _rawRecorder = null; } - _rawRecorder ??= ui.RawRecordingCanvas(size); + final double dpr = window.devicePixelRatio; + _rawRecorder ??= ui.RawRecordingCanvas(ui.Size(size.width / dpr, + size.height / dpr)); // Account for the shift due to padding. _rawRecorder.translate(-BitmapCanvas.kPaddingPixels.toDouble(), -BitmapCanvas.kPaddingPixels.toDouble()); From 1fbf2d2ea7243fd323d3245c7029e2952d508a87 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 9 Apr 2020 22:35:02 -0700 Subject: [PATCH 339/386] Use a sysroot when building Linux host (#17573) Rolls buildroot to enable sysroot for Linux host builds by default. Part of flutter/flutter#53176 --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 761389e3649e0..b8cde7ea62bba 100644 --- a/DEPS +++ b/DEPS @@ -139,7 +139,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '74eedc7f351b2b8c323468c4487b1b5e9b08f0aa', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '0c64fd23eeedbad60c2bfa294710cd587ccb7c27', # Fuchsia compatibility # From 40f32a6736ab160639e3fe11c70f548023e9b298 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 02:15:02 -0400 Subject: [PATCH 340/386] Roll src/third_party/skia 854ac61e90a4..48d345fd83fd (1 commits) (#17627) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index b8cde7ea62bba..194d66c0311c0 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '854ac61e90a49a55146ed85ab364804320ca3476', + 'skia_revision': '48d345fd83fd8860661cf3a3f5e57c5a61b471e7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 94bc7d3739db8..0824217941542 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 22874acfdd6dbdcd7b49b1089739ad65 +Signature: 5fae1aed5c2bfbdd9f0196509edd41d0 UNUSED LICENSES: From a8af96da661da21c9bd8a58600f61090de184ed1 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Fri, 10 Apr 2020 00:16:33 -0700 Subject: [PATCH 341/386] Read SkSLs from asset (#17601) Fixes https://github.com/flutter/flutter/issues/53117 Test added: - ShellTest.CanLoadSkSLsFromAsset --- fml/log_settings.cc | 9 ++ fml/log_settings.h | 9 ++ shell/common/persistent_cache.cc | 31 +++++-- shell/common/persistent_cache.h | 6 ++ shell/common/persistent_cache_unittests.cc | 95 ++++++++++++++++++++++ shell/common/run_configuration.cc | 2 + shell/common/shell.cc | 2 + shell/common/shell_test.cc | 25 ++++-- shell/common/shell_test.h | 13 ++- shell/common/shell_unittests.cc | 4 +- testing/fuchsia/run_tests.sh | 5 +- 11 files changed, 184 insertions(+), 17 deletions(-) diff --git a/fml/log_settings.cc b/fml/log_settings.cc index d70525a72e089..057de5351e453 100644 --- a/fml/log_settings.cc +++ b/fml/log_settings.cc @@ -34,4 +34,13 @@ int GetMinLogLevel() { return std::min(state::g_log_settings.min_log_level, LOG_FATAL); } +ScopedSetLogSettings::ScopedSetLogSettings(const LogSettings& settings) { + old_settings_ = GetLogSettings(); + SetLogSettings(settings); +} + +ScopedSetLogSettings::~ScopedSetLogSettings() { + SetLogSettings(old_settings_); +} + } // namespace fml diff --git a/fml/log_settings.h b/fml/log_settings.h index 2cd14e028c313..cce9ce7bd90d7 100644 --- a/fml/log_settings.h +++ b/fml/log_settings.h @@ -35,6 +35,15 @@ LogSettings GetLogSettings(); // higher than LOG_FATAL. int GetMinLogLevel(); +class ScopedSetLogSettings { + public: + ScopedSetLogSettings(const LogSettings& settings); + ~ScopedSetLogSettings(); + + private: + LogSettings old_settings_; +}; + } // namespace fml #endif // FLUTTER_FML_LOG_SETTINGS_H_ diff --git a/shell/common/persistent_cache.cc b/shell/common/persistent_cache.cc index f663b081bad99..0537905ed22f0 100644 --- a/shell/common/persistent_cache.cc +++ b/shell/common/persistent_cache.cc @@ -20,6 +20,7 @@ namespace flutter { std::string PersistentCache::cache_base_path_; +std::string PersistentCache::asset_path_; std::mutex PersistentCache::instance_mutex_; std::unique_ptr PersistentCache::gPersistentCache; @@ -90,7 +91,7 @@ static std::shared_ptr MakeCacheDirectory( std::vector components = { "flutter_engine", GetFlutterEngineVersion(), "skia", GetSkiaVersion()}; if (cache_sksl) { - components.push_back("sksl"); + components.push_back(PersistentCache::kSkSLSubdirName); } return std::make_shared( CreateDirectory(cache_base_dir, components, @@ -105,9 +106,6 @@ static std::shared_ptr MakeCacheDirectory( std::vector PersistentCache::LoadSkSLs() { TRACE_EVENT0("flutter", "PersistentCache::LoadSkSLs"); std::vector result; - if (!IsValid()) { - return result; - } fml::FileVisitor visitor = [&result](const fml::UniqueFD& directory, const std::string& filename) { std::pair decode_result = fml::Base32Decode(filename); @@ -126,7 +124,25 @@ std::vector PersistentCache::LoadSkSLs() { } return true; }; - fml::VisitFiles(*sksl_cache_directory_, visitor); + + // Only visit sksl_cache_directory_ if this persistent cache is valid. + // However, we'd like to continue visit the asset dir even if this persistent + // cache is invalid. + if (IsValid()) { + fml::VisitFiles(*sksl_cache_directory_, visitor); + } + + fml::UniqueFD root_asset_dir = fml::OpenDirectory(asset_path_.c_str(), false, + fml::FilePermission::kRead); + fml::UniqueFD sksl_asset_dir = + fml::OpenDirectoryReadOnly(root_asset_dir, kSkSLSubdirName); + if (sksl_asset_dir.is_valid()) { + FML_LOG(INFO) << "Found sksl asset directory. Loading SkSLs from it..."; + fml::VisitFiles(sksl_asset_dir, visitor); + } else { + FML_LOG(INFO) << "No sksl asset directory found."; + } + return result; } @@ -283,4 +299,9 @@ fml::RefPtr PersistentCache::GetWorkerTaskRunner() const { return worker; } +void PersistentCache::UpdateAssetPath(const std::string& path) { + FML_LOG(INFO) << "PersistentCache::UpdateAssetPath: " << path; + asset_path_ = path; +} + } // namespace flutter diff --git a/shell/common/persistent_cache.h b/shell/common/persistent_cache.h index c48627fdbfcfa..5cfb0afb75bdb 100644 --- a/shell/common/persistent_cache.h +++ b/shell/common/persistent_cache.h @@ -65,12 +65,18 @@ class PersistentCache : public GrContextOptions::PersistentCache { /// Load all the SkSL shader caches in the right directory. std::vector LoadSkSLs(); + /// Update the asset path from which PersistentCache can load SkLSs. + static void UpdateAssetPath(const std::string& path); + static bool cache_sksl() { return cache_sksl_; } static void SetCacheSkSL(bool value); static void MarkStrategySet() { strategy_set_ = true; } + static constexpr char kSkSLSubdirName[] = "sksl"; + private: static std::string cache_base_path_; + static std::string asset_path_; static std::mutex instance_mutex_; static std::unique_ptr gPersistentCache; diff --git a/shell/common/persistent_cache_unittests.cc b/shell/common/persistent_cache_unittests.cc index 4948a3359bc69..597727af3b8ae 100644 --- a/shell/common/persistent_cache_unittests.cc +++ b/shell/common/persistent_cache_unittests.cc @@ -10,6 +10,7 @@ #include "flutter/flow/layers/picture_layer.h" #include "flutter/fml/command_line.h" #include "flutter/fml/file.h" +#include "flutter/fml/log_settings.h" #include "flutter/fml/unique_fd.h" #include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/shell_test.h" @@ -123,5 +124,99 @@ TEST_F(ShellTest, CacheSkSLWorks) { DestroyShell(std::move(shell)); } +static void CheckTextSkData(sk_sp data, const std::string& expected) { + std::string data_string(reinterpret_cast(data->bytes()), + data->size()); + ASSERT_EQ(data_string, expected); +} + +void ResetAssetPath() { + PersistentCache::UpdateAssetPath("some_path_that_does_not_exist"); + ASSERT_EQ(PersistentCache::GetCacheForProcess()->LoadSkSLs().size(), 0u); +} + +void CheckTwoSkSLsAreLoaded() { + auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + ASSERT_EQ(shaders.size(), 2u); +} + +TEST_F(ShellTest, CanLoadSkSLsFromAsset) { + // Avoid polluting unit tests output by hiding INFO level logging. + fml::LogSettings warning_only = {fml::LOG_WARNING}; + fml::ScopedSetLogSettings scoped_set_log_settings(warning_only); + + // Create an empty shell to test its service protocol handlers. + auto empty_settings = CreateSettingsForFixture(); + auto empty_config = RunConfiguration::InferFromSettings(empty_settings); + std::unique_ptr empty_shell = CreateShell(empty_settings); + + // Temp dir for the asset. + fml::ScopedTemporaryDirectory asset_dir; + fml::UniqueFD sksl_asset_dir = + fml::OpenDirectory(asset_dir.fd(), PersistentCache::kSkSLSubdirName, true, + fml::FilePermission::kReadWrite); + + // The SkSL filenames are Base32 encoded strings. "IE" is the encoding of "A" + // and "II" is the encoding of "B". + const std::string kFileNames[2] = {"IE", "II"}; + const std::string kFileData[2] = {"x", "y"}; + + // Prepare 2 SkSL files in the asset directory. + for (int i = 0; i < 2; i += 1) { + auto data = std::make_unique( + std::vector{kFileData[i].begin(), kFileData[i].end()}); + fml::WriteAtomically(sksl_asset_dir, kFileNames[i].c_str(), *data); + } + + // 1st, test that RunConfiguration::InferFromSettings sets the path. + ResetAssetPath(); + auto settings = CreateSettingsForFixture(); + settings.assets_path = asset_dir.path(); + RunConfiguration::InferFromSettings(settings); + CheckTwoSkSLsAreLoaded(); + + // 2nd, test that Shell::OnServiceProtocolSetAssetBundlePath sets the path. + ResetAssetPath(); + ServiceProtocol::Handler::ServiceProtocolMap params; + rapidjson::Document document; + params["assetDirectory"] = asset_dir.path(); + OnServiceProtocol( + empty_shell.get(), ShellTest::ServiceProtocolEnum::kSetAssetBundlePath, + empty_shell->GetTaskRunners().GetUITaskRunner(), params, document); + CheckTwoSkSLsAreLoaded(); + + // 3rd, test that Shell::OnServiceProtocolRunInView sets the path. + ResetAssetPath(); + params["assetDirectory"] = asset_dir.path(); + params["mainScript"] = "no_such_script.dart"; + OnServiceProtocol( + empty_shell.get(), ShellTest::ServiceProtocolEnum::kSetAssetBundlePath, + empty_shell->GetTaskRunners().GetUITaskRunner(), params, document); + CheckTwoSkSLsAreLoaded(); + + // 4th, test the content of the SkSLs in the asset. + { + auto shaders = PersistentCache::GetCacheForProcess()->LoadSkSLs(); + ASSERT_EQ(shaders.size(), 2u); + + // Make sure that the 2 shaders are sorted by their keys. Their keys should + // be "A" and "B" (decoded from "II" and "IE"). + if (shaders[0].first->bytes()[0] == 'B') { + std::swap(shaders[0], shaders[1]); + } + + CheckTextSkData(shaders[0].first, "A"); + CheckTextSkData(shaders[1].first, "B"); + CheckTextSkData(shaders[0].second, "x"); + CheckTextSkData(shaders[1].second, "y"); + } + + // Cleanup. + DestroyShell(std::move(empty_shell)); + fml::UnlinkFile(sksl_asset_dir, kFileNames[0].c_str()); + fml::UnlinkFile(sksl_asset_dir, kFileNames[1].c_str()); + fml::UnlinkDirectory(asset_dir.fd(), PersistentCache::kSkSLSubdirName); +} + } // namespace testing } // namespace flutter diff --git a/shell/common/run_configuration.cc b/shell/common/run_configuration.cc index de0aa1adc9126..e87c1f0bb5717 100644 --- a/shell/common/run_configuration.cc +++ b/shell/common/run_configuration.cc @@ -10,6 +10,7 @@ #include "flutter/fml/file.h" #include "flutter/fml/unique_fd.h" #include "flutter/runtime/dart_vm.h" +#include "flutter/shell/common/persistent_cache.h" namespace flutter { @@ -26,6 +27,7 @@ RunConfiguration RunConfiguration::InferFromSettings( asset_manager->PushBack( std::make_unique(fml::OpenDirectory( settings.assets_path.c_str(), false, fml::FilePermission::kRead))); + PersistentCache::UpdateAssetPath(settings.assets_path); return {IsolateConfiguration::InferFromSettings(settings, asset_manager, io_worker), diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 71ab4a95094d2..16a76bb9a9eaa 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1312,6 +1312,7 @@ bool Shell::OnServiceProtocolRunInView( configuration.AddAssetResolver( std::make_unique(fml::OpenDirectory( asset_directory_path.c_str(), false, fml::FilePermission::kRead))); + PersistentCache::UpdateAssetPath(asset_directory_path); auto& allocator = response.GetAllocator(); response.SetObject(); @@ -1406,6 +1407,7 @@ bool Shell::OnServiceProtocolSetAssetBundlePath( asset_manager->PushFront(std::make_unique( fml::OpenDirectory(params.at("assetDirectory").data(), false, fml::FilePermission::kRead))); + PersistentCache::UpdateAssetPath(params.at("assetDirectory").data()); if (engine_->UpdateAssetManager(std::move(asset_manager))) { response.AddMember("type", "Success", allocator); diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 378375c5eac1e..07ae5b3f7f93c 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -202,17 +202,28 @@ bool ShellTest::GetNeedsReportTimings(Shell* shell) { return shell->needs_report_timings_; } -void ShellTest::OnServiceProtocolGetSkSLs( +void ShellTest::OnServiceProtocol( Shell* shell, + ServiceProtocolEnum some_protocol, + fml::RefPtr task_runner, const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { std::promise finished; - fml::TaskRunner::RunNowOrPostTask(shell->GetTaskRunners().GetIOTaskRunner(), - [shell, params, &response, &finished]() { - shell->OnServiceProtocolGetSkSLs( - params, response); - finished.set_value(true); - }); + fml::TaskRunner::RunNowOrPostTask( + task_runner, [shell, some_protocol, params, &response, &finished]() { + switch (some_protocol) { + case ServiceProtocolEnum::kGetSkSLs: + shell->OnServiceProtocolGetSkSLs(params, response); + break; + case ServiceProtocolEnum::kSetAssetBundlePath: + shell->OnServiceProtocolSetAssetBundlePath(params, response); + break; + case ServiceProtocolEnum::kRunInView: + shell->OnServiceProtocolRunInView(params, response); + break; + } + finished.set_value(true); + }); finished.get_future().wait(); } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 680db7d349c1d..92c917c0a05da 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -13,6 +13,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/window/platform_message.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/common/thread_host.h" @@ -75,11 +76,19 @@ class ShellTest : public ThreadTest { static bool GetNeedsReportTimings(Shell* shell); static void SetNeedsReportTimings(Shell* shell, bool value); + enum ServiceProtocolEnum { + kGetSkSLs, + kSetAssetBundlePath, + kRunInView, + }; + // Helper method to test private method Shell::OnServiceProtocolGetSkSLs. // (ShellTest is a friend class of Shell.) We'll also make sure that it is - // running on the UI thread. - static void OnServiceProtocolGetSkSLs( + // running on the correct task_runner. + static void OnServiceProtocol( Shell* shell, + ServiceProtocolEnum some_protocol, + fml::RefPtr task_runner, const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response); diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index c9c1345541fcc..b847a4427791f 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1154,7 +1154,9 @@ TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) { std::unique_ptr shell = CreateShell(settings); ServiceProtocol::Handler::ServiceProtocolMap empty_params; rapidjson::Document document; - OnServiceProtocolGetSkSLs(shell.get(), empty_params, document); + OnServiceProtocol(shell.get(), ServiceProtocolEnum::kGetSkSLs, + shell->GetTaskRunners().GetIOTaskRunner(), empty_params, + document); rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); document.Accept(writer); diff --git a/testing/fuchsia/run_tests.sh b/testing/fuchsia/run_tests.sh index 5b40eb4e846fb..7fa6d38b68e77 100755 --- a/testing/fuchsia/run_tests.sh +++ b/testing/fuchsia/run_tests.sh @@ -80,9 +80,10 @@ echo "$(date) START:runtime_tests -----------------------------------" -t runtime_tests # TODO(https://github.com/flutter/flutter/issues/53399): Re-enable -# OnServiceProtocolGetSkSLsWorks once it passes on Fuchsia. +# OnServiceProtocolGetSkSLsWorks and CanLoadSkSLsFromAsset once they pass on +# Fuchsia. echo "$(date) START:shell_tests -------------------------------------" ./fuchsia_ctl -d $device_name test \ -f shell_tests-0.far \ -t shell_tests \ - -a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks" + -a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks:ShellTest.CanLoadSkSLsFromAsset" From 9d86b2fe1c4ee5124c8bd76eb2d516ed8193173d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 04:10:03 -0400 Subject: [PATCH 342/386] Roll src/third_party/skia 48d345fd83fd..05e2350de5a9 (3 commits) (#17629) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 194d66c0311c0..cfacfbb2844d7 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '48d345fd83fd8860661cf3a3f5e57c5a61b471e7', + 'skia_revision': '05e2350de5a97a4f2adc91af9ad292538fd7fa10', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 0824217941542..ec46a3fab6317 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 5fae1aed5c2bfbdd9f0196509edd41d0 +Signature: c84295942006d570da529064ab954cc5 UNUSED LICENSES: From d184c508b531979a6ec2fc831dcf264029abbbad Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 05:55:03 -0400 Subject: [PATCH 343/386] Roll src/third_party/dart 9f65693f5772..9a51d7003dd5 (2 commits) (#17630) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index cfacfbb2844d7..0a351aba57ea0 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '9f65693f5772f9b859a16baa2381462920abbee9', + 'dart_revision': '9a51d7003dd559d97354670bb1e2660a679a5065', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 06da9ba39154de5777644ce0c193590196986776 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 06:15:03 -0400 Subject: [PATCH 344/386] Roll fuchsia/sdk/core/mac-amd64 from REQ-c... to teyLc... (#17631) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 0a351aba57ea0..6b91712d4a812 100644 --- a/DEPS +++ b/DEPS @@ -535,7 +535,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'REQ-c4gnypoLDrJp8vxMd6VeQXzD3xHdgjWRPwShcHcC' + 'version': 'teyLcp5ejqE62omH_EVKbOMXuwUztpXORt1-muDEOCUC' } ], 'condition': 'host_os == "mac"', From ecd4c1c4132c1dc468234b1addf83a31ffbd9722 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 11:00:03 -0400 Subject: [PATCH 345/386] Roll src/third_party/skia 05e2350de5a9..f7255d72f8da (1 commits) (#17633) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6b91712d4a812..d2e04c2a95599 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '05e2350de5a97a4f2adc91af9ad292538fd7fa10', + 'skia_revision': 'f7255d72f8da16153cb3d794a4b6802494f390e2', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index ec46a3fab6317..5a8a995d3850e 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c84295942006d570da529064ab954cc5 +Signature: 1983cff4d8ec59e14bc39c63a8530880 UNUSED LICENSES: From 6aa702959675a7542c6eb24fb1ca6060743f074c Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 10 Apr 2020 09:02:52 -0700 Subject: [PATCH 346/386] Eliminate fx_log_init call (#17622) fx_log_init was already a no-op, and was eliminated in the latest Fuchsia SDK. See: https://fuchsia.googlesource.com/fuchsia/+/47e568475f535c00f53c86e19ff4d8736a7b261f --- shell/platform/fuchsia/dart_runner/main.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/fuchsia/dart_runner/main.cc b/shell/platform/fuchsia/dart_runner/main.cc index 2dfe8d418106b..a6fb17904bc25 100644 --- a/shell/platform/fuchsia/dart_runner/main.cc +++ b/shell/platform/fuchsia/dart_runner/main.cc @@ -31,7 +31,6 @@ static void RegisterProfilerSymbols(const char* symbols_path, #endif // !defined(DART_PRODUCT) int main(int argc, const char** argv) { - fx_log_init(); async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread); std::unique_ptr provider; From 49f7cd04ac576dded03375365a818f21aef1a92d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 12:55:04 -0400 Subject: [PATCH 347/386] Roll src/third_party/skia f7255d72f8da..96bfeff55c58 (4 commits) (#17638) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d2e04c2a95599..6669e50e93490 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f7255d72f8da16153cb3d794a4b6802494f390e2', + 'skia_revision': '96bfeff55c58f7fcd44b51fd123e73e72b3c36e7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 5a8a995d3850e..442e57cc1331a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1983cff4d8ec59e14bc39c63a8530880 +Signature: 31f2194a6eaf186f5f0692405a055049 UNUSED LICENSES: From 52afb7dfde41e996c77774861b4c8abf4b266491 Mon Sep 17 00:00:00 2001 From: godofredoc <54371434+godofredoc@users.noreply.github.com> Date: Fri, 10 Apr 2020 10:42:41 -0700 Subject: [PATCH 348/386] Remove duplicated cirrus tasks. (#17625) Some cirrus tasks are also running on LUCI. We are removing duplicated tasks to free up cirrus capacity. https://github.com/flutter/flutter/issues/54412 --- .cirrus.yml | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 5dbeef441a142..c38fbb5b85941 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -212,40 +212,3 @@ task: cd $ENGINE_PATH/src ./flutter/tools/fuchsia/build_fuchsia_artifacts.py --engine-version HEAD --runtime-mode debug --no-lto --archs x64 cd $ENGINE_PATH/src/flutter - -# WINDOWS -task: - gce_instance: - image_project: flutter-cirrus - image_name: flutter-engine-windows-server-2016 - zone: us-central1-a - platform: windows - cpu: 32 - memory: 32Gb - disk: 50 - env: - # Cirrus is somehow not picking up the environment variables set in the VM image. - PATH: "c:/depot_tools;c:/MinGit/cmd;$PATH" - DEPOT_TOOLS_WIN_TOOLCHAIN: 0 - GYP_MSVS_OVERRIDE_PATH: "c:/Program Files (x86)/Microsoft Visual Studio/2017/Community" - ENGINE_PATH: "c:/flutter/engine" - setup_script: | - REM robocopy can return 1 for successful copy; suppress its error code. - REM move somehow doesn't work as it complains that the file is being used by another process. - robocopy %CIRRUS_WORKING_DIR% %ENGINE_PATH%/src/flutter /MIR| (cmd /s /c exit /b 0) - cd %ENGINE_PATH%/src - gclient sync - matrix: - - name: build_and_test_windows_unopt_debug - compile_host_script: | - cd %ENGINE_PATH%/src - python flutter/tools/gn --runtime-mode debug --unoptimized - ninja -C out/host_debug_unopt - test_host_script: | - cd %ENGINE_PATH%/src - python flutter/testing/run_tests.py --type=engine - - name: build_windows_opt_debug - compile_host_script: | - cd %ENGINE_PATH%/src - python flutter/tools/gn --runtime-mode debug - ninja -C out/host_debug From adb4f9e9751b83d422277043b9163088764ca9d9 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 10 Apr 2020 12:41:20 -0700 Subject: [PATCH 349/386] Roll fuchsia/sdk/core/linux-amd64 from VzWN4... to LnaL2... (#17640) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-linux-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6669e50e93490..0372a2f68aebf 100644 --- a/DEPS +++ b/DEPS @@ -555,7 +555,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'VzWN4x5phvAfhb4PT5j9mgzeNIGwxlfQxJHkhiKnligC' + 'version': 'LnaL23_DpQsbnbs-byJi-UoGe1XerKCfLjb4_XkxMRoC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index f2d6b002fd63c..5e24346dc2215 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 4f1e4147fee10dda274e4946a42065f6 +Signature: adc98c90de1d424da0f0546ff7689965 UNUSED LICENSES: @@ -3277,6 +3277,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/pointer.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/update.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/url.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/auth.fidl +FILE: ../../../fuchsia/sdk/linux/pkg/fidl_base/validate_string.cc FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/commands_sizing.cc FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands_sizing.h FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/stream.h From 2aa03f4dc388327c726fe73d966b62b5394c2cf5 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 16:15:04 -0400 Subject: [PATCH 350/386] Roll src/third_party/skia 96bfeff55c58..801ba0d6064f (8 commits) (#17639) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 0372a2f68aebf..31cd5483c3fe6 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '96bfeff55c58f7fcd44b51fd123e73e72b3c36e7', + 'skia_revision': '801ba0d6064f03569407d1d98e314a47c3e31cc8', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 442e57cc1331a..2c2fcd29904d5 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 31f2194a6eaf186f5f0692405a055049 +Signature: d7f2bc134b3c51934aba12ca103ba06c UNUSED LICENSES: From 68fd8334889610af08cbfc2828f91cf14faf3f1d Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 10 Apr 2020 13:25:02 -0700 Subject: [PATCH 351/386] Reland "Improve iOS PlatformViews to better handle thread merging. #16935" (#17609) --- ci/licenses_golden/licenses_flutter | 2 + flow/embedded_views.h | 12 ++++ shell/common/BUILD.gn | 2 + shell/common/animator_unittests.cc | 2 +- shell/common/rasterizer.cc | 10 +++ shell/common/shell_test.cc | 16 +++-- shell/common/shell_test.h | 10 ++- .../shell_test_external_view_embedder.cc | 55 ++++++++++++++ .../shell_test_external_view_embedder.h | 72 +++++++++++++++++++ shell/common/shell_test_platform_view.cc | 10 ++- shell/common/shell_test_platform_view.h | 5 +- shell/common/shell_test_platform_view_gl.cc | 9 ++- shell/common/shell_test_platform_view_gl.h | 8 ++- .../common/shell_test_platform_view_vulkan.cc | 23 ++++-- .../common/shell_test_platform_view_vulkan.h | 16 ++++- shell/common/shell_unittests.cc | 48 ++++++++++++- .../framework/Source/FlutterPlatformViews.mm | 33 ++++++++- .../Source/FlutterPlatformViews_Internal.h | 12 ++++ shell/platform/darwin/ios/ios_surface.h | 3 + shell/platform/darwin/ios/ios_surface.mm | 28 +++++++- 20 files changed, 345 insertions(+), 31 deletions(-) create mode 100644 shell/common/shell_test_external_view_embedder.cc create mode 100644 shell/common/shell_test_external_view_embedder.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 453b653dc57a5..c5830d31d4aac 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -592,6 +592,8 @@ FILE: ../../../flutter/shell/common/shell_io_manager.cc FILE: ../../../flutter/shell/common/shell_io_manager.h FILE: ../../../flutter/shell/common/shell_test.cc FILE: ../../../flutter/shell/common/shell_test.h +FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.cc +FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.h FILE: ../../../flutter/shell/common/shell_test_platform_view.cc FILE: ../../../flutter/shell/common/shell_test_platform_view.h FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.cc diff --git a/flow/embedded_views.h b/flow/embedded_views.h index b80f7b4ba5d54..14dd00cf5e8c8 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -253,6 +253,18 @@ class ExternalViewEmbedder { // This is called after submitting the embedder frame and the surface frame. virtual void FinishFrame(); + // This should only be called after |SubmitFrame|. + // This method provides the embedder a way to do additional tasks after + // |SubmitFrame|. After invoking this method, the current task on the + // TaskRunner should end immediately. + // + // For example on the iOS embedder, threads are merged in this call. + // A new frame on the platform thread starts immediately. If the GPU thread + // still has some task running, there could be two frames being rendered + // concurrently, which causes undefined behaviors. + virtual void EndFrame( + fml::RefPtr raster_thread_merger) {} + FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); }; // ExternalViewEmbedder diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 66684679c3de3..3fab3317b094b 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -191,6 +191,8 @@ if (enable_unittests) { "pipeline_unittests.cc", "shell_test.cc", "shell_test.h", + "shell_test_external_view_embedder.cc", + "shell_test_external_view_embedder.h", "shell_test_platform_view.cc", "shell_test_platform_view.h", "shell_unittests.cc", diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index ed54ea5272be2..bf2ac9f5a9cf1 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -55,7 +55,7 @@ TEST_F(ShellTest, VSyncTargetTime) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index bce1b2bf036cb..ce456925c69ac 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -136,6 +136,16 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } + // Merging the thread as we know the next `Draw` should be run on the platform + // thread. + if (raster_status == RasterStatus::kResubmit) { + auto* external_view_embedder = surface_->GetExternalViewEmbedder(); + // We know only the `external_view_embedder` can + // causes|RasterStatus::kResubmit|. Check to make sure. + FML_DCHECK(external_view_embedder != nullptr); + external_view_embedder->EndFrame(raster_thread_merger_); + } + // Consume as many pipeline items as possible. But yield the event loop // between successive tries. switch (consume_result) { diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 07ae5b3f7f93c..8ef045cf0a045 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -271,9 +271,12 @@ std::unique_ptr ShellTest::CreateShell(Settings settings, simulate_vsync); } -std::unique_ptr ShellTest::CreateShell(Settings settings, - TaskRunners task_runners, - bool simulate_vsync) { +std::unique_ptr ShellTest::CreateShell( + Settings settings, + TaskRunners task_runners, + bool simulate_vsync, + std::shared_ptr + shell_test_external_view_embedder) { const auto vsync_clock = std::make_shared(); CreateVsyncWaiter create_vsync_waiter = [&]() { if (simulate_vsync) { @@ -286,17 +289,18 @@ std::unique_ptr ShellTest::CreateShell(Settings settings, }; return Shell::Create( task_runners, settings, - [vsync_clock, &create_vsync_waiter](Shell& shell) { + [vsync_clock, &create_vsync_waiter, + shell_test_external_view_embedder](Shell& shell) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, + shell_test_external_view_embedder); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); }); } - void ShellTest::DestroyShell(std::unique_ptr shell) { DestroyShell(std::move(shell), GetTaskRunnersForFixture()); } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 92c917c0a05da..caf055120f0a0 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -16,6 +16,7 @@ #include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiters_test.h" #include "flutter/testing/elf_loader.h" @@ -32,9 +33,12 @@ class ShellTest : public ThreadTest { Settings CreateSettingsForFixture(); std::unique_ptr CreateShell(Settings settings, bool simulate_vsync = false); - std::unique_ptr CreateShell(Settings settings, - TaskRunners task_runners, - bool simulate_vsync = false); + std::unique_ptr CreateShell( + Settings settings, + TaskRunners task_runners, + bool simulate_vsync = false, + std::shared_ptr + shell_test_external_view_embedder = nullptr); void DestroyShell(std::unique_ptr shell); void DestroyShell(std::unique_ptr shell, TaskRunners task_runners); TaskRunners GetTaskRunnersForFixture(); diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc new file mode 100644 index 0000000000000..683e21ad73c91 --- /dev/null +++ b/shell/common/shell_test_external_view_embedder.cc @@ -0,0 +1,55 @@ +#include "shell_test_external_view_embedder.h" + +namespace flutter { + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::CancelFrame() {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::BeginFrame(SkISize frame_size, + GrContext* context, + double device_pixel_ratio) {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) {} + +// |ExternalViewEmbedder| +PostPrerollResult ShellTestExternalViewEmbedder::PostPrerollAction( + fml::RefPtr raster_thread_merger) { + FML_DCHECK(raster_thread_merger); + return post_preroll_result_; +} + +// |ExternalViewEmbedder| +std::vector ShellTestExternalViewEmbedder::GetCurrentCanvases() { + return {}; +} + +// |ExternalViewEmbedder| +SkCanvas* ShellTestExternalViewEmbedder::CompositeEmbeddedView(int view_id) { + return nullptr; +} + +// |ExternalViewEmbedder| +bool ShellTestExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { + return true; +} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::FinishFrame() {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::EndFrame( + fml::RefPtr raster_thread_merger) { + end_frame_call_back_(); +} + +// |ExternalViewEmbedder| +SkCanvas* ShellTestExternalViewEmbedder::GetRootCanvas() { + return nullptr; +} + +} // namespace flutter diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h new file mode 100644 index 0000000000000..56d9123ea8a7a --- /dev/null +++ b/shell/common/shell_test_external_view_embedder.h @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ +#define FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ + +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/raster_thread_merger.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// @brief The external view embedder used by |ShellTestPlatformViewGL| +/// +class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { + public: + using EndFrameCallBack = std::function; + + ShellTestExternalViewEmbedder(const EndFrameCallBack& end_frame_call_back, + PostPrerollResult post_preroll_result) + : end_frame_call_back_(end_frame_call_back), + post_preroll_result_(post_preroll_result) {} + + ~ShellTestExternalViewEmbedder() = default; + + private: + // |ExternalViewEmbedder| + void CancelFrame() override; + + // |ExternalViewEmbedder| + void BeginFrame(SkISize frame_size, + GrContext* context, + double device_pixel_ratio) override; + + // |ExternalViewEmbedder| + void PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) override; + + // |ExternalViewEmbedder| + PostPrerollResult PostPrerollAction( + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + + // |ExternalViewEmbedder| + SkCanvas* CompositeEmbeddedView(int view_id) override; + + // |ExternalViewEmbedder| + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; + + // |ExternalViewEmbedder| + void EndFrame( + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + SkCanvas* GetRootCanvas() override; + + const EndFrameCallBack end_frame_call_back_; + const PostPrerollResult post_preroll_result_; + + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestExternalViewEmbedder); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ diff --git a/shell/common/shell_test_platform_view.cc b/shell/common/shell_test_platform_view.cc index cfd43308bfc10..29bd24d3134ff 100644 --- a/shell/common/shell_test_platform_view.cc +++ b/shell/common/shell_test_platform_view.cc @@ -19,7 +19,9 @@ std::unique_ptr ShellTestPlatformView::Create( TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend) { + BackendType backend, + std::shared_ptr + shell_test_external_view_embedder) { // TODO(gw280): https://github.com/flutter/flutter/issues/50298 // Make this fully runtime configurable switch (backend) { @@ -27,12 +29,14 @@ std::unique_ptr ShellTestPlatformView::Create( #ifdef SHELL_ENABLE_GL case BackendType::kGLBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter); + delegate, task_runners, vsync_clock, create_vsync_waiter, + shell_test_external_view_embedder); #endif // SHELL_ENABLE_GL #ifdef SHELL_ENABLE_VULKAN case BackendType::kVulkanBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter); + delegate, task_runners, vsync_clock, create_vsync_waiter, + shell_test_external_view_embedder); #endif // SHELL_ENABLE_VULKAN default: FML_LOG(FATAL) << "No backends supported for ShellTestPlatformView"; diff --git a/shell/common/shell_test_platform_view.h b/shell/common/shell_test_platform_view.h index c7aefe86b33bc..9a850b1d481d6 100644 --- a/shell/common/shell_test_platform_view.h +++ b/shell/common/shell_test_platform_view.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_H_ #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/vsync_waiters_test.h" namespace flutter { @@ -24,7 +25,9 @@ class ShellTestPlatformView : public PlatformView { TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend); + BackendType backend, + std::shared_ptr + shell_test_external_view_embedder); virtual void SimulateVSync() = 0; diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 2bd597575e523..c980c53168645 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -12,11 +12,14 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter) + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder) : ShellTestPlatformView(delegate, std::move(task_runners)), gl_surface_(SkISize::Make(800, 600)), create_vsync_waiter_(std::move(create_vsync_waiter)), - vsync_clock_(vsync_clock) {} + vsync_clock_(vsync_clock), + shell_test_external_view_embedder_(shell_test_external_view_embedder) {} ShellTestPlatformViewGL::~ShellTestPlatformViewGL() = default; @@ -70,7 +73,7 @@ ShellTestPlatformViewGL::GetGLProcResolver() const { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* ShellTestPlatformViewGL::GetExternalViewEmbedder() { - return nullptr; + return shell_test_external_view_embedder_.get(); } } // namespace testing diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index 03db7910873e0..cac2fdc649781 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "flutter/testing/test_gl_surface.h" @@ -18,7 +19,9 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, ShellTestPlatformViewGL(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter); + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder); virtual ~ShellTestPlatformViewGL() override; @@ -31,6 +34,9 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, std::shared_ptr vsync_clock_; + std::shared_ptr + shell_test_external_view_embedder_; + // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index e9f41484d9881..4c21c2dfba36e 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -11,11 +11,14 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter) + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder) : ShellTestPlatformView(delegate, std::move(task_runners)), create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(vsync_clock), - proc_table_(fml::MakeRefCounted()) {} + proc_table_(fml::MakeRefCounted()), + shell_test_external_view_embedder_(shell_test_external_view_embedder) {} ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; @@ -29,7 +32,8 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface() { - return std::make_unique(proc_table_); + return std::make_unique(proc_table_, + shell_test_external_view_embedder_); } // |PlatformView| @@ -44,8 +48,12 @@ PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { // We need to merge this functionality back into //vulkan. // https://github.com/flutter/flutter/issues/51132 ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( - fml::RefPtr vk) - : valid_(false), vk_(std::move(vk)) { + fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder) + : valid_(false), + vk_(std::move(vk)), + shell_test_external_view_embedder_(shell_test_external_view_embedder) { if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; @@ -170,5 +178,10 @@ SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() return matrix; } +flutter::ExternalViewEmbedder* +ShellTestPlatformViewVulkan::OffScreenSurface::GetExternalViewEmbedder() { + return shell_test_external_view_embedder_.get(); +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 38bce87fa5414..31757f39a30b0 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/vulkan/vulkan_application.h" @@ -18,7 +19,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { ShellTestPlatformViewVulkan(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter); + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder); ~ShellTestPlatformViewVulkan() override; @@ -27,7 +30,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { private: class OffScreenSurface : public flutter::Surface { public: - OffScreenSurface(fml::RefPtr vk); + OffScreenSurface(fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder); ~OffScreenSurface() override; @@ -42,9 +47,13 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |Surface| GrContext* GetContext() override; + flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; + private: bool valid_; fml::RefPtr vk_; + std::shared_ptr + shell_test_external_view_embedder_; std::unique_ptr application_; std::unique_ptr logical_device_; sk_sp context_; @@ -61,6 +70,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { fml::RefPtr proc_table_; + std::shared_ptr + shell_test_external_view_embedder_; + // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index b847a4427791f..e96ab7c441b99 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -23,6 +23,7 @@ #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_test.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" @@ -139,7 +140,7 @@ TEST_F(ShellTest, return static_cast>( std::make_unique(task_runners)); }, - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); @@ -466,6 +467,51 @@ TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { DestroyShell(std::move(shell)); } +TEST_F(ShellTest, + ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit) { + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent endFrameLatch; + bool end_frame_called = false; + auto end_frame_callback = [&] { + end_frame_called = true; + endFrameLatch.Signal(); + }; + auto external_view_embedder = std::make_shared( + end_frame_callback, PostPrerollResult::kResubmitFrame); + auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), + false, external_view_embedder); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + + LayerTreeBuilder builder = [&](std::shared_ptr root) { + SkPictureRecorder recorder; + SkCanvas* recording_canvas = + recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80)); + recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80), + SkPaint(SkColor4f::FromColor(SK_ColorRED))); + auto sk_picture = recorder.finishRecordingAsPicture(); + fml::RefPtr queue = fml::MakeRefCounted( + this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + auto picture_layer = std::make_shared( + SkPoint::Make(10, 10), + flutter::SkiaGPUObject({sk_picture, queue}), false, false); + root->Add(picture_layer); + }; + + PumpOneFrame(shell.get(), 100, 100, builder); + endFrameLatch.Wait(); + + ASSERT_TRUE(end_frame_called); + + DestroyShell(std::move(shell)); +} + TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { // Ensure that all phases are in kPhases. ASSERT_EQ(sizeof(FrameTiming::kPhases), diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 3fbaa7dd8a935..14a45142eb9b3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -248,10 +248,13 @@ } void FlutterPlatformViewsController::CancelFrame() { - composition_order_.clear(); + composition_order_ = active_composition_order_; } bool FlutterPlatformViewsController::HasPendingViewOperations() { + if (!views_to_dispose_.empty()) { + return true; + } if (!views_to_recomposite_.empty()) { return true; } @@ -267,8 +270,9 @@ if (raster_thread_merger->IsMerged()) { raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); } else { + // Wait until |EndFrame| to merge the threads. + merge_threads_ = true; CancelFrame(); - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); return PostPrerollResult::kResubmitFrame; } } @@ -468,6 +472,21 @@ bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context, SkCanvas* background_canvas) { + if (merge_threads_) { + // Threads are about to be merged, we drop everything from this frame + // and possibly resubmit the same layer tree in the next frame. + // Before merging thread, we know the code is not running on the main thread. Assert that + FML_DCHECK(![[NSThread currentThread] isMainThread]); + picture_recorders_.clear(); + composition_order_.clear(); + return true; + } + + // Any UIKit related code has to run on main thread. + // When on a non-main thread, we only allow the rest of the method to run if there is no + // Pending UIView operations. + FML_DCHECK([[NSThread currentThread] isMainThread] || !HasPendingViewOperations()); + DisposeViews(); // Resolve all pending GPU operations before allocating a new surface. @@ -578,6 +597,14 @@ } } +void FlutterPlatformViewsController::EndFrame( + fml::RefPtr raster_thread_merger) { + if (merge_threads_) { + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); + merge_threads_ = false; + } +} + std::shared_ptr FlutterPlatformViewsController::GetLayer( GrContext* gr_context, std::shared_ptr ios_context, @@ -642,6 +669,8 @@ return; } + FML_DCHECK([[NSThread currentThread] isMainThread]); + for (int64_t viewId : views_to_dispose_) { UIView* root_view = root_views_[viewId].get(); [root_view removeFromSuperview]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index e998640777c96..0dcb793b00f03 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -134,6 +134,8 @@ class FlutterPlatformViewsController { void SetFrameSize(SkISize frame_size); + // Indicates that we don't compisite any platform views or overlays during this frame. + // Also reverts the composition_order_ to its original state at the begining of the frame. void CancelFrame(); void PrerollCompositeEmbeddedView(int view_id, @@ -163,6 +165,11 @@ class FlutterPlatformViewsController { std::shared_ptr ios_context, SkCanvas* background_canvas); + // Invoked at the very end of a frame. + // After invoking this method, nothing should happen on the current TaskRunner during the same + // frame. + void EndFrame(fml::RefPtr raster_thread_merger); + void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: @@ -273,6 +280,10 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + // Default to `false`. + // If `true`, gpu thread and platform thread should be merged during |EndFrame|. + // Always resets to `false` right after the threads are merged. + bool merge_threads_ = false; // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from // the picture on the layer's canvas. std::shared_ptr GetLayer(GrContext* gr_context, @@ -282,6 +293,7 @@ class FlutterPlatformViewsController { int64_t view_id, int64_t overlay_id); // Removes overlay views and platform views that aren't needed in the current frame. + // Must run on the platform thread. void RemoveUnusedLayers(); // Appends the overlay views and platform view and sets their z index based on the composition // order. diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index fd1bc7abb0b48..eae39031d77ce 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -83,6 +83,9 @@ class IOSSurface : public ExternalViewEmbedder { // |ExternalViewEmbedder| void FinishFrame() override; + // |ExternalViewEmbedder| + void EndFrame(fml::RefPtr raster_thread_merger) override; + public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); }; diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index cbe2cb4fb8c2c..5f2725273e0dd 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -90,7 +90,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { platform_views_controller_->CancelFrame(); // Committing the current transaction as |BeginFrame| will create a nested // CATransaction otherwise. - [CATransaction commit]; + if ([[NSThread currentThread] isMainThread]) { + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. + [CATransaction commit]; + } } // |ExternalViewEmbedder| @@ -98,7 +103,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { TRACE_EVENT0("flutter", "IOSSurface::BeginFrame"); FML_CHECK(platform_views_controller_ != nullptr); platform_views_controller_->SetFrameSize(frame_size); - [CATransaction begin]; + if ([[NSThread currentThread] isMainThread]) { + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. + [CATransaction begin]; + } } // |ExternalViewEmbedder| @@ -140,10 +150,22 @@ bool IsIosEmbeddedViewsPreviewEnabled() { return submitted; } +// |ExternalViewEmbedder| +void IOSSurface::EndFrame(fml::RefPtr raster_thread_merger) { + TRACE_EVENT0("flutter", "IOSSurface::EndFrame"); + FML_CHECK(platform_views_controller_ != nullptr); + return platform_views_controller_->EndFrame(raster_thread_merger); +} + // |ExternalViewEmbedder| void IOSSurface::FinishFrame() { TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + if (![[NSThread currentThread] isMainThread]) { + return; + } + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. [CATransaction commit]; } - } // namespace flutter From c69f21a3493c0277926d2b153ecc407c421521d9 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 10 Apr 2020 13:30:41 -0700 Subject: [PATCH 352/386] [web] Combine duplicate platform message spy implementations (#17615) --- lib/web_ui/test/spy.dart | 2 +- lib/web_ui/test/text_editing_test.dart | 85 ++++++++++++-------------- 2 files changed, 39 insertions(+), 48 deletions(-) diff --git a/lib/web_ui/test/spy.dart b/lib/web_ui/test/spy.dart index e044a890eef84..ed7596e7313ad 100644 --- a/lib/web_ui/test/spy.dart +++ b/lib/web_ui/test/spy.dart @@ -23,7 +23,7 @@ class PlatformMessage { String get methodName => methodCall.method; /// Shorthand for getting the arguments of the method call. - String get methodArguments => methodCall.arguments; + dynamic get methodArguments => methodCall.arguments; } /// Intercepts platform messages sent from the engine to the framework. diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index 97360e41357eb..6b64aaef49e77 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -7,12 +7,12 @@ import 'dart:html'; import 'dart:js_util' as js_util; import 'dart:typed_data'; -import 'package:ui/ui.dart' as ui; import 'package:ui/src/engine.dart' hide window; import 'package:test/test.dart'; import 'matchers.dart'; +import 'spy.dart'; /// The `keyCode` of the "Enter" key. const int _kReturnKeyCode = 13; @@ -597,11 +597,11 @@ void main() { setUp(() { textEditing = HybridTextEditing(); - spy.activate(); + spy.setUp(); }); tearDown(() { - spy.deactivate(); + spy.tearDown(); if (textEditing.isEditing) { textEditing.stopEditing(); } @@ -699,15 +699,15 @@ void main() { textEditing.editingElement.domElement.blur(); expect(spy.messages, hasLength(1)); - MethodCall call = spy.messages[0]; - spy.messages.clear(); - expect(call.method, 'TextInputClient.onConnectionClosed'); + expect(spy.messages[0].channel, 'flutter/textinput'); + expect(spy.messages[0].methodName, 'TextInputClient.onConnectionClosed'); expect( - call.arguments, + spy.messages[0].methodArguments, [ 123, // Client ID ], ); + spy.messages.clear(); // Input element is removed from DOM. expect(document.getElementsByTagName('input'), hasLength(0)); }, @@ -998,11 +998,10 @@ void main() { input.dispatchEvent(Event.eventType('Event', 'input')); expect(spy.messages, hasLength(1)); - MethodCall call = spy.messages[0]; - spy.messages.clear(); - expect(call.method, 'TextInputClient.updateEditingState'); + expect(spy.messages[0].channel, 'flutter/textinput'); + expect(spy.messages[0].methodName, 'TextInputClient.updateEditingState'); expect( - call.arguments, + spy.messages[0].methodArguments, [ 123, // Client ID { @@ -1012,6 +1011,7 @@ void main() { } ], ); + spy.messages.clear(); input.setSelectionRange(2, 5); if (browserEngine == BrowserEngine.firefox) { @@ -1022,11 +1022,10 @@ void main() { } expect(spy.messages, hasLength(1)); - call = spy.messages[0]; - spy.messages.clear(); - expect(call.method, 'TextInputClient.updateEditingState'); + expect(spy.messages[0].channel, 'flutter/textinput'); + expect(spy.messages[0].methodName, 'TextInputClient.updateEditingState'); expect( - call.arguments, + spy.messages[0].methodArguments, [ 123, // Client ID { @@ -1036,6 +1035,7 @@ void main() { } ], ); + spy.messages.clear(); hideKeyboard(); }); @@ -1078,20 +1078,35 @@ void main() { // Two messages should've been sent. One for the 'input' event and one for // the 'selectionchange' event. expect(spy.messages, hasLength(2)); - final MethodCall call = spy.messages.last; - spy.messages.clear(); - expect(call.method, 'TextInputClient.updateEditingState'); + + expect(spy.messages[0].channel, 'flutter/textinput'); + expect(spy.messages[0].methodName, 'TextInputClient.updateEditingState'); + expect( + spy.messages[0].methodArguments, + [ + 123, // Client ID + { + 'text': 'something\nelse', + 'selectionBase': 14, + 'selectionExtent': 14, + } + ], + ); + + expect(spy.messages[1].channel, 'flutter/textinput'); + expect(spy.messages[1].methodName, 'TextInputClient.updateEditingState'); expect( - call.arguments, + spy.messages[1].methodArguments, [ 123, // Client ID { 'text': 'something\nelse', 'selectionBase': 2, - 'selectionExtent': 5 + 'selectionExtent': 5, } ], ); + spy.messages.clear(); const MethodCall hide = MethodCall('TextInput.hide'); sendFrameworkMessage(codec.encodeMethodCall(hide)); @@ -1171,10 +1186,10 @@ void main() { ); expect(spy.messages, hasLength(1)); - final MethodCall call = spy.messages.first; - expect(call.method, 'TextInputClient.performAction'); + expect(spy.messages[0].channel, 'flutter/textinput'); + expect(spy.messages[0].methodName, 'TextInputClient.performAction'); expect( - call.arguments, + spy.messages[0].methodArguments, [clientId, 'TextInputAction.next'], ); }, @@ -1384,30 +1399,6 @@ void checkTextAreaEditingState( expect(textarea.selectionEnd, end); } -class PlatformMessagesSpy { - bool _isActive = false; - ui.PlatformMessageCallback _backup; - - final List messages = []; - - void activate() { - assert(!_isActive); - _isActive = true; - _backup = ui.window.onPlatformMessage; - ui.window.onPlatformMessage = (String channel, ByteData data, - ui.PlatformMessageResponseCallback callback) { - messages.add(codec.decodeMethodCall(data)); - }; - } - - void deactivate() { - assert(_isActive); - _isActive = false; - messages.clear(); - ui.window.onPlatformMessage = _backup; - } -} - Map createFlutterConfig( String inputType, { bool obscureText = false, From bed48056a651b7f6d2b6a3294373e6fa1800cd9e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 18:30:02 -0400 Subject: [PATCH 353/386] Roll src/third_party/skia 801ba0d6064f..daf94c56bcb3 (4 commits) (#17644) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 31cd5483c3fe6..9c9fa04804b8c 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '801ba0d6064f03569407d1d98e314a47c3e31cc8', + 'skia_revision': 'daf94c56bcb37ef052f0dfba8cb1736a8ab52813', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2c2fcd29904d5..e2181910b34fb 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: d7f2bc134b3c51934aba12ca103ba06c +Signature: f1da467776d98711fe0647399aee8a68 UNUSED LICENSES: From 2d19576f247894a8dc39c9bf10617952f041a587 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 10 Apr 2020 16:00:01 -0700 Subject: [PATCH 354/386] Implement Hashcode for TextEditingValue in InputConnectionAdaptor (#17643) --- .../plugin/editing/InputConnectionAdaptor.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index 525e2efe8f847..509f82f7e0926 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -75,6 +75,18 @@ public boolean equals(Object o) { && composingEnd == value.composingEnd && text.equals(value.text); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + selectionStart; + result = prime * result + selectionEnd; + result = prime * result + composingStart; + result = prime * result + composingEnd; + result = prime * result + text.hashCode(); + return result; + } } @SuppressWarnings("deprecation") From d6c1398a3f06afe7fd9c0a68587b7d88ec84df39 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 19:55:05 -0400 Subject: [PATCH 355/386] Roll src/third_party/dart 9a51d7003dd5..9e2c8ef0009b (20 commits) (#17648) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 9c9fa04804b8c..17a8e7306c725 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '9a51d7003dd559d97354670bb1e2660a679a5065', + 'dart_revision': '9e2c8ef0009b9e30cfc5806e389f89fe1e0b6052', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 5be3eb40d2658..c297bf1bcf7cf 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 5378c4c7f1771d4587cd7f93a9f22ad2 +Signature: 6740e884f25483acb772155a8609c45d UNUSED LICENSES: From f779f09058c695f33eaee1f4b7dcd8908ac5f195 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 10 Apr 2020 22:35:05 -0400 Subject: [PATCH 356/386] Roll fuchsia/sdk/core/mac-amd64 from teyLc... to zCKPv... (#17651) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 17a8e7306c725..68fca92b66294 100644 --- a/DEPS +++ b/DEPS @@ -535,7 +535,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'teyLcp5ejqE62omH_EVKbOMXuwUztpXORt1-muDEOCUC' + 'version': 'zCKPvsueceqBCaNV0iSuGUljmzilg7wYD_NZJPixOiEC' } ], 'condition': 'host_os == "mac"', From 71d8edb13adf65dea6d619fd409b383746c75f57 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sat, 11 Apr 2020 11:50:03 -0700 Subject: [PATCH 357/386] Always build GTK shell (#17634) --- DEPS | 5 +- ci/licenses_golden/licenses_gpu | 42 +++ ci/licenses_golden/licenses_third_party | 372 ++++++++++++++++-------- shell/platform/linux/BUILD.gn | 85 +++--- shell/platform/linux/config.gni | 11 - shell/platform/linux/config/BUILD.gn | 14 +- sky/packages/sky_engine/LICENSE | 286 ++++++++++++------ 7 files changed, 536 insertions(+), 279 deletions(-) create mode 100644 ci/licenses_golden/licenses_gpu delete mode 100644 shell/platform/linux/config.gni diff --git a/DEPS b/DEPS index 68fca92b66294..bcae0b6b9cc05 100644 --- a/DEPS +++ b/DEPS @@ -139,7 +139,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '0c64fd23eeedbad60c2bfa294710cd587ccb7c27', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '036715c76da60220b39312ea066cd65d32c2157d', # Fuchsia compatibility # @@ -179,6 +179,9 @@ deps = { 'src/third_party/icu': Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '5005010d694e16571b8dfbf07d70817841f80a69', + 'src/third_party/khronos': + Var('chromium_git') + '/chromium/src/third_party/khronos.git' + '@' + '7122230e90547962e0f0c627f62eeed3c701f275', + 'src/third_party/boringssl': Var('github_git') + '/dart-lang/boringssl_gen.git' + '@' + Var('dart_boringssl_gen_rev'), diff --git a/ci/licenses_golden/licenses_gpu b/ci/licenses_golden/licenses_gpu new file mode 100644 index 0000000000000..a1f8f8e87988c --- /dev/null +++ b/ci/licenses_golden/licenses_gpu @@ -0,0 +1,42 @@ +Signature: ffe64a3daaf0ad982854594ad155dd56 + +UNUSED LICENSES: + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +USED LICENSES: + +==================================================================================================== +LIBRARY: gpu +ORIGIN: ../../../flutter/LICENSE +TYPE: LicenseType.bsd +FILE: ../../../gpu/GLES2/gl2chromium.h +FILE: ../../../gpu/command_buffer/client/gles2_c_lib_export.h +---------------------------------------------------------------------------------------------------- +Copyright 2013 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +==================================================================================================== +Total license count: 1 diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index c297bf1bcf7cf..7c5f56a45adb0 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 6740e884f25483acb772155a8609c45d +Signature: 8740c9b5018b2a4f028cfd44e7e33204 UNUSED LICENSES: @@ -542,6 +542,7 @@ POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== LIBRARY: angle LIBRARY: boringssl +LIBRARY: khronos LIBRARY: observatory_pub_packages LIBRARY: vulkan LIBRARY: wuffs @@ -664,6 +665,8 @@ FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/qu FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/time/util.dart FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/strings.dart FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/time.dart +FILE: ../../../third_party/khronos/GLES2/gl2platform.h +FILE: ../../../third_party/khronos/GLES3/gl3platform.h FILE: ../../../third_party/vulkan/include/vulkan/vk_platform.h FILE: ../../../third_party/vulkan/include/vulkan/vulkan.h FILE: ../../../third_party/vulkan/include/vulkan/vulkan_android.h @@ -1016,6 +1019,138 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================================================== +==================================================================================================== +LIBRARY: angle +LIBRARY: khronos +ORIGIN: ../../../third_party/angle/include/EGL/egl.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/include/EGL/egl.h +FILE: ../../../third_party/angle/include/EGL/eglext.h +FILE: ../../../third_party/angle/include/GLES/glext.h +FILE: ../../../third_party/angle/include/GLES2/gl2.h +FILE: ../../../third_party/angle/scripts/egl.xml +FILE: ../../../third_party/khronos/EGL/egl.h +FILE: ../../../third_party/khronos/EGL/eglext.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013-2017 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +LIBRARY: khronos +ORIGIN: ../../../third_party/angle/include/EGL/eglplatform.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/include/EGL/eglplatform.h +FILE: ../../../third_party/khronos/EGL/eglplatform.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2007-2016 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +LIBRARY: khronos +ORIGIN: ../../../third_party/angle/include/GLES/gl.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/include/GLES/gl.h +FILE: ../../../third_party/angle/include/GLES2/gl2ext.h +FILE: ../../../third_party/angle/include/GLES3/gl3.h +FILE: ../../../third_party/angle/include/WGL/wgl.h +FILE: ../../../third_party/khronos/GLES2/gl2.h +FILE: ../../../third_party/khronos/GLES2/gl2ext.h +FILE: ../../../third_party/khronos/GLES3/gl3.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013-2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: angle +LIBRARY: khronos +ORIGIN: ../../../third_party/angle/include/GLES3/gl31.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/angle/include/GLES3/gl31.h +FILE: ../../../third_party/angle/include/GLES3/gl32.h +FILE: ../../../third_party/khronos/GLES3/gl31.h +FILE: ../../../third_party/khronos/GLES3/gl32.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2013-2016 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + ==================================================================================================== LIBRARY: angle LIBRARY: vulkan-validation-layers @@ -1947,126 +2082,6 @@ ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/include/EGL/egl.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/EGL/egl.h -FILE: ../../../third_party/angle/include/EGL/eglext.h -FILE: ../../../third_party/angle/include/GLES/glext.h -FILE: ../../../third_party/angle/include/GLES2/gl2.h -FILE: ../../../third_party/angle/scripts/egl.xml ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013-2017 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== - -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/include/EGL/eglplatform.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/EGL/eglplatform.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2007-2016 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== - -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/include/GLES/gl.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/GLES/gl.h -FILE: ../../../third_party/angle/include/GLES2/gl2ext.h -FILE: ../../../third_party/angle/include/GLES3/gl3.h -FILE: ../../../third_party/angle/include/WGL/wgl.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013-2018 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== - -==================================================================================================== -LIBRARY: angle -ORIGIN: ../../../third_party/angle/include/GLES3/gl31.h -TYPE: LicenseType.unknown -FILE: ../../../third_party/angle/include/GLES3/gl31.h -FILE: ../../../third_party/angle/include/GLES3/gl32.h ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013-2016 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -==================================================================================================== - ==================================================================================================== LIBRARY: angle ORIGIN: ../../../third_party/angle/include/GLSLANG/ShaderLang.h + ../../../third_party/angle/LICENSE @@ -17668,6 +17683,8 @@ LIBRARY: khronos ORIGIN: ../../../third_party/angle/src/third_party/khronos/GL/wglext.h TYPE: LicenseType.unknown FILE: ../../../third_party/angle/src/third_party/khronos/GL/wglext.h +FILE: ../../../third_party/khronos/noninclude/GL/glext.h +FILE: ../../../third_party/khronos/noninclude/GL/wglext.h ---------------------------------------------------------------------------------------------------- Copyright (c) 2013-2014 The Khronos Group Inc. @@ -17691,6 +17708,117 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ==================================================================================================== +==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/KHR/khrplatform.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/khronos/KHR/khrplatform.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2008-2009 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + +==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/LICENSE +TYPE: LicenseType.mit +FILE: ../../../third_party/khronos/DEPS +---------------------------------------------------------------------------------------------------- +Copyright (c) 2007-2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + +Copyright (C) 1992 Silicon Graphics, Inc. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice including the dates of first publication and either +this permission notice or a reference to http://oss.sgi.com/projects/FreeB +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON +GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Silicon Graphics, Inc. shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from Silicon +Graphics, Inc. +==================================================================================================== + +==================================================================================================== +LIBRARY: khronos +ORIGIN: ../../../third_party/khronos/noninclude/GL/glxext.h +TYPE: LicenseType.unknown +FILE: ../../../third_party/khronos/noninclude/GL/glxext.h +---------------------------------------------------------------------------------------------------- +Copyright (c) 2007-2012 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +==================================================================================================== + ==================================================================================================== LIBRARY: libXNVCtrl ORIGIN: ../../../third_party/angle/src/third_party/libXNVCtrl/LICENSE @@ -23626,4 +23754,4 @@ freely, subject to the following restrictions: misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ==================================================================================================== -Total license count: 367 +Total license count: 370 diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 1b09d54aed6af..d5ac69df74e6a 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -5,10 +5,12 @@ assert(is_linux) import("//flutter/shell/platform/glfw/config.gni") -import("//flutter/shell/platform/linux/config.gni") group("linux") { - deps = [] + deps = [ + ":flutter_linux_gtk", + ":publish_headers_linux", + ] if (build_glfw_shell) { deps += [ ":flutter_linux_glfw", @@ -16,12 +18,6 @@ group("linux") { "//flutter/shell/platform/glfw/client_wrapper:publish_wrapper_glfw", ] } - if (build_linux_shell) { - deps += [ - ":flutter_linux_gtk", - ":publish_headers_linux", - ] - } } # Temporary workaround for the issue describe in @@ -46,50 +42,49 @@ if (build_glfw_shell) { } } -if (build_linux_shell) { - _public_headers = [ - "public/flutter_linux/fl_dart_project.h", - "public/flutter_linux/fl_view.h", - "public/flutter_linux/flutter_linux.h", - ] +_public_headers = [ + "public/flutter_linux/fl_dart_project.h", + "public/flutter_linux/fl_view.h", + "public/flutter_linux/flutter_linux.h", +] - config("relative_flutter_linux_headers") { - include_dirs = [ "public" ] - } +config("relative_flutter_linux_headers") { + include_dirs = [ "public" ] +} - source_set("flutter_linux") { - public = _public_headers +source_set("flutter_linux") { + public = _public_headers - sources = [ - "fl_dart_project.cc", - "fl_view.cc", - ] + sources = [ + "fl_dart_project.cc", + "fl_view.cc", + ] - configs += [ - "//flutter/shell/platform/linux/config:gtk", - "//flutter/shell/platform/linux/config:egl", - ] + configs += [ + "//flutter/shell/platform/linux/config:gtk", + "//flutter/shell/platform/linux/config:egl", + "//third_party/khronos:khronos_headers", + ] - # Set flag to stop headers being directly included (library users should not do this) - defines = [ "FLUTTER_LINUX_COMPILATION" ] + # Set flag to stop headers being directly included (library users should not do this) + defines = [ "FLUTTER_LINUX_COMPILATION" ] - deps = [ - "//flutter/shell/platform/embedder:embedder_with_symbol_prefix", - ] - } + deps = [ + "//flutter/shell/platform/embedder:embedder_with_symbol_prefix", + ] +} - shared_library("flutter_linux_gtk") { - deps = [ - ":flutter_linux", - ] +shared_library("flutter_linux_gtk") { + deps = [ + ":flutter_linux", + ] - public_configs = [ "//flutter:config" ] - } + public_configs = [ "//flutter:config" ] +} - copy("publish_headers_linux") { - sources = _public_headers - outputs = [ - "$root_out_dir/flutter_linux/{{source_file_part}}", - ] - } +copy("publish_headers_linux") { + sources = _public_headers + outputs = [ + "$root_out_dir/flutter_linux/{{source_file_part}}", + ] } diff --git a/shell/platform/linux/config.gni b/shell/platform/linux/config.gni deleted file mode 100644 index 012f3d5438b44..0000000000000 --- a/shell/platform/linux/config.gni +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2013 The Flutter Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -declare_args() { - # Whether to build the Linux (GTK) shell for the host platform, if available. - # - # The Linux shell is not currently built by default as the CI system doesn't - # (yet) have GTK as a dependency. When that is ready the flag will be removed. - build_linux_shell = false -} diff --git a/shell/platform/linux/config/BUILD.gn b/shell/platform/linux/config/BUILD.gn index 19f8001db2fb5..d188f2dd6389b 100644 --- a/shell/platform/linux/config/BUILD.gn +++ b/shell/platform/linux/config/BUILD.gn @@ -3,17 +3,15 @@ # found in the LICENSE file. import("//build/config/linux/pkg_config.gni") -import("//flutter/shell/platform/linux/config.gni") pkg_config("x11") { packages = [ "x11" ] } -if (build_linux_shell) { - pkg_config("gtk") { - packages = [ "gtk+-3.0" ] - } - pkg_config("egl") { - packages = [ "egl" ] - } +pkg_config("gtk") { + packages = [ "gtk+-3.0" ] +} + +pkg_config("egl") { + packages = [ "egl" ] } diff --git a/sky/packages/sky_engine/LICENSE b/sky/packages/sky_engine/LICENSE index 17d812e9a5e1f..fca14e2785127 100644 --- a/sky/packages/sky_engine/LICENSE +++ b/sky/packages/sky_engine/LICENSE @@ -54,29 +54,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- angle -Copyright (c) 2007-2016 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. --------------------------------------------------------------------------------- -angle - Copyright (c) 2008-2018 The Khronos Group Inc. Permission is hereby granted, free of charge, to any person obtaining a @@ -100,75 +77,6 @@ MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -------------------------------------------------------------------------------- angle -Copyright (c) 2013-2016 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. --------------------------------------------------------------------------------- -angle - -Copyright (c) 2013-2017 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. --------------------------------------------------------------------------------- -angle - -Copyright (c) 2013-2018 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. --------------------------------------------------------------------------------- -angle - Copyright 2002 The ANGLE Project Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -514,6 +422,7 @@ angle boringssl engine etc1 +khronos observatory_pub_packages txt vulkan @@ -753,6 +662,102 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- angle +khronos + +Copyright (c) 2007-2016 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +-------------------------------------------------------------------------------- +angle +khronos + +Copyright (c) 2013-2016 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +-------------------------------------------------------------------------------- +angle +khronos + +Copyright (c) 2013-2017 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +-------------------------------------------------------------------------------- +angle +khronos + +Copyright (c) 2013-2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +-------------------------------------------------------------------------------- +angle vulkan-validation-layers Copyright 2018 The ANGLE Project Authors. All rights reserved. @@ -4390,6 +4395,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- engine +gpu tonic txt @@ -8621,6 +8627,102 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- khronos +Copyright (c) 2007-2010 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + +Copyright (C) 1992 Silicon Graphics, Inc. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice including the dates of first publication and either +this permission notice or a reference to http://oss.sgi.com/projects/FreeB +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SILICON +GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Silicon Graphics, Inc. shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from Silicon +Graphics, Inc. +-------------------------------------------------------------------------------- +khronos + +Copyright (c) 2007-2012 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +-------------------------------------------------------------------------------- +khronos + +Copyright (c) 2008-2009 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +-------------------------------------------------------------------------------- +khronos + Copyright (c) 2013-2014 The Khronos Group Inc. Permission is hereby granted, free of charge, to any person obtaining a From 47a88e8ea3e09fd494f3cf82260274eca0a2af0a Mon Sep 17 00:00:00 2001 From: Mehmet Fidanboylu Date: Sat, 11 Apr 2020 11:55:05 -0700 Subject: [PATCH 358/386] Add support for setting allow http flag in Dart VM (#17653) --- common/settings.h | 4 ++++ lib/io/BUILD.gn | 1 + lib/io/dart_io.cc | 18 ++++++++++++++---- lib/io/dart_io.h | 2 +- runtime/dart_isolate.cc | 5 +++-- runtime/dart_isolate.h | 1 + shell/common/switches.cc | 3 +++ shell/common/switches.h | 6 ++++++ 8 files changed, 33 insertions(+), 7 deletions(-) diff --git a/common/settings.h b/common/settings.h index c0914ae9970a7..45f851d3543a8 100644 --- a/common/settings.h +++ b/common/settings.h @@ -99,6 +99,10 @@ struct Settings { bool endless_trace_buffer = false; bool enable_dart_profiling = false; bool disable_dart_asserts = false; + + // Used to signal the embedder whether HTTP connections are disabled. + bool disable_http = false; + // Used as the script URI in debug messages. Does not affect how the Dart code // is executed. std::string advisory_script_uri = "main.dart"; diff --git a/lib/io/BUILD.gn b/lib/io/BUILD.gn index 27cdc99eaa429..529872c30d23c 100644 --- a/lib/io/BUILD.gn +++ b/lib/io/BUILD.gn @@ -9,6 +9,7 @@ source_set("io") { ] deps = [ + "//flutter/fml", "//flutter/third_party/tonic", "//third_party/dart/runtime:dart_api", "//third_party/dart/runtime/bin:dart_io_api", diff --git a/lib/io/dart_io.cc b/lib/io/dart_io.cc index 70dd8d1f1c630..6e5e538d74da0 100644 --- a/lib/io/dart_io.cc +++ b/lib/io/dart_io.cc @@ -4,21 +4,31 @@ #include "flutter/lib/io/dart_io.h" +#include "flutter/fml/logging.h" + #include "third_party/dart/runtime/include/bin/dart_io_api.h" #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/logging/dart_error.h" +using tonic::LogIfError; using tonic::ToDart; namespace flutter { -void DartIO::InitForIsolate() { +void DartIO::InitForIsolate(bool disable_http) { Dart_Handle result = Dart_SetNativeResolver( Dart_LookupLibrary(ToDart("dart:io")), dart::bin::LookupIONative, dart::bin::LookupIONativeSymbol); - if (Dart_IsError(result)) { - Dart_PropagateError(result); - } + FML_CHECK(!LogIfError(result)); + + // The SDK expects this field to represent "allow http" so we switch the + // value. + Dart_Handle allow_http_value = disable_http ? Dart_False() : Dart_True(); + Dart_Handle set_field_result = + Dart_SetField(Dart_LookupLibrary(ToDart("dart:_http")), + ToDart("_embedderAllowsHttp"), allow_http_value); + FML_CHECK(!LogIfError(set_field_result)); } } // namespace flutter diff --git a/lib/io/dart_io.h b/lib/io/dart_io.h index 10fe07b514744..27ce7aa65baeb 100644 --- a/lib/io/dart_io.h +++ b/lib/io/dart_io.h @@ -13,7 +13,7 @@ namespace flutter { class DartIO { public: - static void InitForIsolate(); + static void InitForIsolate(bool disable_http); private: FML_DISALLOW_IMPLICIT_CONSTRUCTORS(DartIO); diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index c8d7940c7baed..3e78de7473e38 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -137,7 +137,8 @@ DartIsolate::DartIsolate(const Settings& settings, settings.log_tag, settings.unhandled_exception_callback, DartVMRef::GetIsolateNameServer()), - is_root_isolate_(is_root_isolate) { + is_root_isolate_(is_root_isolate), + disable_http_(settings.disable_http) { phase_ = Phase::Uninitialized; } @@ -261,7 +262,7 @@ bool DartIsolate::LoadLibraries() { tonic::DartState::Scope scope(this); - DartIO::InitForIsolate(); + DartIO::InitForIsolate(disable_http_); DartUI::InitForIsolate(IsRootIsolate()); diff --git a/runtime/dart_isolate.h b/runtime/dart_isolate.h index 22f6ea5b98bd0..4a045d38d422c 100644 --- a/runtime/dart_isolate.h +++ b/runtime/dart_isolate.h @@ -402,6 +402,7 @@ class DartIsolate : public UIDartState { std::vector> shutdown_callbacks_; fml::RefPtr message_handling_task_runner_; const bool is_root_isolate_; + const bool disable_http_; DartIsolate(const Settings& settings, TaskRunners task_runners, diff --git a/shell/common/switches.cc b/shell/common/switches.cc index 885763a107557..0f0c7fc145a78 100644 --- a/shell/common/switches.cc +++ b/shell/common/switches.cc @@ -237,6 +237,9 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { } } + settings.disable_http = + command_line.HasOption(FlagForSwitch(Switch::DisableHttp)); + // Disable need for authentication codes for VM service communication, if // specified. settings.disable_service_auth_codes = diff --git a/shell/common/switches.h b/shell/common/switches.h index abf1b3ae2997a..f261a76e141e1 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -174,6 +174,12 @@ DEF_SWITCH(DisableDartAsserts, "disabled. This flag may be specified if the user wishes to run " "with assertions disabled in the debug product mode (i.e. with JIT " "or DBC).") +DEF_SWITCH(DisableHttp, + "disable-http", + "Dart VM has a master switch that can be set to disable insecure " + "HTTP and WebSocket protocols. Localhost or loopback addresses are " + "exempted. This flag can be specified if the embedder wants this " + "for a particular platform.") DEF_SWITCH( ForceMultithreading, "force-multithreading", From f0e489600aca9381625a84ca465b46ce9c663782 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 11 Apr 2020 15:00:02 -0400 Subject: [PATCH 359/386] Roll src/third_party/skia daf94c56bcb3..459ecee2cbdc (1 commits) (#17655) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index bcae0b6b9cc05..a46f68e9025eb 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'daf94c56bcb37ef052f0dfba8cb1736a8ab52813', + 'skia_revision': '459ecee2cbdc2bad7a3c750af64ebff194c73ea5', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index e2181910b34fb..2477efa18bb12 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: f1da467776d98711fe0647399aee8a68 +Signature: 058ee3f539b545b97fc0ec27768fa9ab UNUSED LICENSES: From e66389398e36af25436ad7c7a6bc22032fc26360 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 11 Apr 2020 18:50:02 -0400 Subject: [PATCH 360/386] Roll src/third_party/dart 9e2c8ef0009b..89b0f6726123 (8 commits) (#17658) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index a46f68e9025eb..6971b2c22a528 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '9e2c8ef0009b9e30cfc5806e389f89fe1e0b6052', + 'dart_revision': '89b0f67261232822da1d2cf818b3c2bbe8fa58e2', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -62,7 +62,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.16.1', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.114', + 'dart_linter_tag': '0.1.115', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.5', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 7c5f56a45adb0..67b0ccbb6a6b6 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 8740c9b5018b2a4f028cfd44e7e33204 +Signature: ca93d840288cbd1c9634445936ba08e1 UNUSED LICENSES: From beb8a7ec48f6b980a778d19eeda613635c3897c9 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Sat, 11 Apr 2020 17:29:50 -0700 Subject: [PATCH 361/386] Update editing state in InputConnectionAdaptor.setSelection (#17652) The BaseInputConnection superclass does not call endBatchEdit in setSelection and therefore does not implicitly cause InputConnectionAdaptor to send a state update. Some input modes such as numeric keypads will not function without these updates. --- .../io/flutter/plugin/editing/InputConnectionAdaptor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index 509f82f7e0926..d1b58039fb910 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -287,6 +287,7 @@ private boolean isSamsung() { public boolean setSelection(int start, int end) { boolean result = super.setSelection(start, end); markDirty(); + updateEditingState(); return result; } From 9992a4ae664b0de4bf1834ad44dc48c7ee6025e8 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Sat, 11 Apr 2020 19:06:37 -0700 Subject: [PATCH 362/386] Fix C++ MethodChannel reply type (#17607) Makes InvokeMethod's reply a high-level response object, rather than binary data, matching the abstraction level of the class (and the other languages' implementations). In support of that: - Adds the logic to the codecs to decode response envelopes, which had never been implemented. - Adds a convience implementation of MethodResult that forwards to lambdas, so that one-off invocation handlers are easier to write. Also simplified BinaryMessenger's API so that subclasses only need to implement one version of Send, rather than two almost-identical versions. Fixes https://github.com/flutter/flutter/issues/53223 --- ci/licenses_golden/licenses_flutter | 2 + shell/platform/common/cpp/BUILD.gn | 1 + .../common/cpp/client_wrapper/BUILD.gn | 2 +- .../basic_message_channel_unittests.cc | 4 - .../cpp/client_wrapper/core_wrapper_files.gni | 1 + .../include/flutter/binary_messenger.h | 22 ++--- .../include/flutter/method_channel.h | 47 ++++++++--- .../include/flutter/method_codec.h | 26 +++++- .../include/flutter/method_result.h | 4 +- .../include/flutter/method_result_functions.h | 77 ++++++++++++++++++ .../include/flutter/standard_method_codec.h | 8 +- .../method_channel_unittests.cc | 81 +++++++++++++++++-- .../method_result_functions_unittests.cc | 66 +++++++++++++++ .../cpp/client_wrapper/plugin_registrar.cc | 18 +---- .../cpp/client_wrapper/standard_codec.cc | 31 ++++++- .../standard_method_codec_unittests.cc | 61 +++++++++++++- .../platform/common/cpp/json_method_codec.cc | 66 +++++++++++---- shell/platform/common/cpp/json_method_codec.h | 6 ++ .../common/cpp/json_method_codec_unittests.cc | 61 +++++++++++++- 19 files changed, 500 insertions(+), 84 deletions(-) create mode 100644 shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h create mode 100644 shell/platform/common/cpp/client_wrapper/method_result_functions_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c5830d31d4aac..0a686a8a090e9 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -785,12 +785,14 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registry.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc +FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_result_functions_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_codec.cc diff --git a/shell/platform/common/cpp/BUILD.gn b/shell/platform/common/cpp/BUILD.gn index 4d211c8fe8de1..f3bcde6b2442d 100644 --- a/shell/platform/common/cpp/BUILD.gn +++ b/shell/platform/common/cpp/BUILD.gn @@ -116,6 +116,7 @@ executable("common_cpp_unittests") { deps = [ ":common_cpp", ":common_cpp_fixtures", + "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper", "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper_library_stubs", "//flutter/testing", ] diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index ed0bdf9e87536..94e68dc86084a 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -41,12 +41,12 @@ test_fixtures("client_wrapper_fixtures") { executable("client_wrapper_unittests") { testonly = true - # TODO: Add more unit tests. sources = [ "basic_message_channel_unittests.cc", "encodable_value_unittests.cc", "method_call_unittests.cc", "method_channel_unittests.cc", + "method_result_functions_unittests.cc", "plugin_registrar_unittests.cc", "standard_message_codec_unittests.cc", "standard_method_codec_unittests.cc", diff --git a/shell/platform/common/cpp/client_wrapper/basic_message_channel_unittests.cc b/shell/platform/common/cpp/client_wrapper/basic_message_channel_unittests.cc index 3e47b80ceca45..5098e89b30dd6 100644 --- a/shell/platform/common/cpp/client_wrapper/basic_message_channel_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/basic_message_channel_unittests.cc @@ -17,10 +17,6 @@ namespace { class TestBinaryMessenger : public BinaryMessenger { public: - void Send(const std::string& channel, - const uint8_t* message, - const size_t message_size) const override {} - void Send(const std::string& channel, const uint8_t* message, const size_t message_size, diff --git a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni index 0662cca6be422..86c3280ea3699 100644 --- a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni +++ b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni @@ -12,6 +12,7 @@ core_cpp_client_wrapper_includes = "include/flutter/method_call.h", "include/flutter/method_channel.h", "include/flutter/method_codec.h", + "include/flutter/method_result_functions.h", "include/flutter/method_result.h", "include/flutter/plugin_registrar.h", "include/flutter/plugin_registry.h", diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h b/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h index 08d074c86e744..8dd2f78ad8f76 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h @@ -8,21 +8,19 @@ #include #include -// TODO: Consider adding absl as a dependency and using absl::Span for all of -// the message/message_size pairs. namespace flutter { // A binary message reply callback. // // Used for submitting a binary reply back to a Flutter message sender. -typedef std::function +typedef std::function BinaryReply; // A message handler callback. // // Used for receiving messages from Flutter and providing an asynchronous reply. typedef std::function< - void(const uint8_t* message, const size_t message_size, BinaryReply reply)> + void(const uint8_t* message, size_t message_size, BinaryReply reply)> BinaryMessageHandler; // A protocol for a class that handles communication of binary data on named @@ -31,18 +29,14 @@ class BinaryMessenger { public: virtual ~BinaryMessenger() = default; - // Sends a binary message to the Flutter side on the specified channel, - // expecting no reply. - virtual void Send(const std::string& channel, - const uint8_t* message, - const size_t message_size) const = 0; - - // Sends a binary message to the Flutter side on the specified channel, - // expecting a reply. + // Sends a binary message to the Flutter engine on the specified channel. + // + // If |reply| is provided, it will be called back with the response from the + // engine. virtual void Send(const std::string& channel, const uint8_t* message, - const size_t message_size, - BinaryReply reply) const = 0; + size_t message_size, + BinaryReply reply = nullptr) const = 0; // Registers a message handler for incoming binary messages from the Flutter // side on the specified channel. diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h index 96658af96c3c4..0fe94701c963b 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h @@ -44,21 +44,46 @@ class MethodChannel { MethodChannel& operator=(MethodChannel const&) = delete; // Sends a message to the Flutter engine on this channel. - void InvokeMethod(const std::string& method, std::unique_ptr arguments) { - MethodCall method_call(method, std::move(arguments)); - std::unique_ptr> message = - codec_->EncodeMethodCall(method_call); - messenger_->Send(name_, message->data(), message->size(), nullptr); - } - - // Sends a message to the Flutter engine on this channel expecting a reply. + // + // If |result| is provided, one of its methods will be invoked with the + // response from the engine. void InvokeMethod(const std::string& method, std::unique_ptr arguments, - flutter::BinaryReply reply) { + std::unique_ptr> result = nullptr) { MethodCall method_call(method, std::move(arguments)); std::unique_ptr> message = codec_->EncodeMethodCall(method_call); - messenger_->Send(name_, message->data(), message->size(), reply); + if (!result) { + messenger_->Send(name_, message->data(), message->size(), nullptr); + return; + } + + // std::function requires a copyable lambda, so convert to a shared pointer. + // This is safe since only one copy of the shared_pointer will ever be + // accessed. + std::shared_ptr> shared_result(result.release()); + const auto* codec = codec_; + std::string channel_name = name_; + BinaryReply reply_handler = [shared_result, codec, channel_name]( + const uint8_t* reply, size_t reply_size) { + if (reply_size == 0) { + shared_result->NotImplemented(); + return; + } + // Use this channel's codec to decode and handle the + // reply. + bool decoded = codec->DecodeAndProcessResponseEnvelope( + reply, reply_size, shared_result.get()); + if (!decoded) { + std::cerr << "Unable to decode reply to method " + "invocation on channel " + << channel_name << std::endl; + shared_result->NotImplemented(); + } + }; + + messenger_->Send(name_, message->data(), message->size(), + std::move(reply_handler)); } // Registers a handler that should be called any time a method call is @@ -76,7 +101,7 @@ class MethodChannel { std::string channel_name = name_; BinaryMessageHandler binary_handler = [handler, codec, channel_name]( const uint8_t* message, - const size_t message_size, + size_t message_size, BinaryReply reply) { // Use this channel's codec to decode the call and build a result handler. auto result = diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h index 6f9e09ac24775..959e298c5984a 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h @@ -10,6 +10,7 @@ #include #include "method_call.h" +#include "method_result.h" namespace flutter { @@ -28,9 +29,8 @@ class MethodCodec { // Returns the MethodCall encoded in |message|, or nullptr if it cannot be // decoded. - std::unique_ptr> DecodeMethodCall( - const uint8_t* message, - const size_t message_size) const { + std::unique_ptr> DecodeMethodCall(const uint8_t* message, + size_t message_size) const { return std::move(DecodeMethodCallInternal(message, message_size)); } @@ -67,11 +67,23 @@ class MethodCodec { EncodeErrorEnvelopeInternal(error_code, error_message, error_details)); } + // Decodes the response envelope encoded in |response|, calling the + // appropriate method on |result|. + // + // Returns false if |response| cannot be decoded. In that case the caller is + // responsible for calling a |result| method. + bool DecodeAndProcessResponseEnvelope(const uint8_t* response, + size_t response_size, + MethodResult* result) const { + return DecodeAndProcessResponseEnvelopeInternal(response, response_size, + result); + } + protected: // Implementation of the public interface, to be provided by subclasses. virtual std::unique_ptr> DecodeMethodCallInternal( const uint8_t* message, - const size_t message_size) const = 0; + size_t message_size) const = 0; // Implementation of the public interface, to be provided by subclasses. virtual std::unique_ptr> EncodeMethodCallInternal( @@ -86,6 +98,12 @@ class MethodCodec { const std::string& error_code, const std::string& error_message, const T* error_details) const = 0; + + // Implementation of the public interface, to be provided by subclasses. + virtual bool DecodeAndProcessResponseEnvelopeInternal( + const uint8_t* response, + size_t response_size, + MethodResult* result) const = 0; }; } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h index e3bf572996eb4..1e9c822e253e8 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_result.h @@ -9,8 +9,8 @@ namespace flutter { -// Encapsulates a result sent back to the Flutter engine in response to a -// MethodCall. Only one method should be called on any given instance. +// Encapsulates a result returned from a MethodCall. Only one method should be +// called on any given instance. template class MethodResult { public: diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h b/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h new file mode 100644 index 0000000000000..762128f444324 --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h @@ -0,0 +1,77 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_FUNCTIONS_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_FUNCTIONS_H_ + +#include +#include + +#include "method_result.h" + +namespace flutter { + +// Handler types for each of the MethodResult outcomes. +template +using ResultHandlerSuccess = std::function; +template +using ResultHandlerError = std::function; +template +using ResultHandlerNotImplemented = std::function; + +// An implementation of MethodResult that pass calls through to provided +// function objects, for ease of constructing one-off result handlers. +template +class MethodResultFunctions : public MethodResult { + public: + // Creates a result object that calls the provided functions for the + // corresponding MethodResult outcomes. + MethodResultFunctions(ResultHandlerSuccess on_success, + ResultHandlerError on_error, + ResultHandlerNotImplemented on_not_implemented) + : on_success_(on_success), + on_error_(on_error), + on_not_implemented_(on_not_implemented) {} + + virtual ~MethodResultFunctions() = default; + + // Prevent copying. + MethodResultFunctions(MethodResultFunctions const&) = delete; + MethodResultFunctions& operator=(MethodResultFunctions const&) = delete; + + protected: + // |flutter::MethodResult| + void SuccessInternal(const T* result) override { + if (on_success_) { + on_success_(result); + } + } + + // |flutter::MethodResult| + void ErrorInternal(const std::string& error_code, + const std::string& error_message, + const T* error_details) override { + if (on_error_) { + on_error_(error_code, error_message, error_details); + } + } + + // |flutter::MethodResult| + void NotImplementedInternal() override { + if (on_not_implemented_) { + on_not_implemented_(); + } + } + + private: + ResultHandlerSuccess on_success_; + ResultHandlerError on_error_; + ResultHandlerNotImplemented on_not_implemented_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_METHOD_RESULT_FUNCTIONS_H_ diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h index 3b474110613cb..ef40897893183 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h @@ -30,7 +30,7 @@ class StandardMethodCodec : public MethodCodec { // |flutter::MethodCodec| std::unique_ptr> DecodeMethodCallInternal( const uint8_t* message, - const size_t message_size) const override; + size_t message_size) const override; // |flutter::MethodCodec| std::unique_ptr> EncodeMethodCallInternal( @@ -45,6 +45,12 @@ class StandardMethodCodec : public MethodCodec { const std::string& error_code, const std::string& error_message, const EncodableValue* error_details) const override; + + // |flutter::MethodCodec| + bool DecodeAndProcessResponseEnvelopeInternal( + const uint8_t* response, + size_t response_size, + MethodResult* result) const override; }; } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc b/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc index 549c1023a4ab0..ee62b526eaf12 100644 --- a/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc @@ -8,6 +8,7 @@ #include #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h" #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h" #include "gtest/gtest.h" @@ -19,12 +20,11 @@ class TestBinaryMessenger : public BinaryMessenger { public: void Send(const std::string& channel, const uint8_t* message, - const size_t message_size) const override {} - - void Send(const std::string& channel, - const uint8_t* message, - const size_t message_size, - BinaryReply reply) const override {} + size_t message_size, + BinaryReply reply) const override { + send_called_ = true; + last_reply_handler_ = reply; + } void SetMessageHandler(const std::string& channel, BinaryMessageHandler handler) override { @@ -32,6 +32,10 @@ class TestBinaryMessenger : public BinaryMessenger { last_message_handler_ = handler; } + bool send_called() { return send_called_; } + + BinaryReply last_reply_handler() { return last_reply_handler_; } + std::string last_message_handler_channel() { return last_message_handler_channel_; } @@ -39,6 +43,8 @@ class TestBinaryMessenger : public BinaryMessenger { BinaryMessageHandler last_message_handler() { return last_message_handler_; } private: + mutable bool send_called_ = false; + mutable BinaryReply last_reply_handler_; std::string last_message_handler_channel_; BinaryMessageHandler last_message_handler_; }; @@ -71,8 +77,8 @@ TEST(MethodChannelTest, Registration) { messenger.last_message_handler()( message->data(), message->size(), - [](const uint8_t* reply, const size_t reply_size) {}); - EXPECT_EQ(callback_called, true); + [](const uint8_t* reply, size_t reply_size) {}); + EXPECT_TRUE(callback_called); } // Tests that SetMethodCallHandler with a null handler unregisters the handler. @@ -91,4 +97,63 @@ TEST(MethodChannelTest, Unregistration) { EXPECT_EQ(messenger.last_message_handler(), nullptr); } +TEST(MethodChannelTest, InvokeWithoutResponse) { + TestBinaryMessenger messenger; + const std::string channel_name("some_channel"); + MethodChannel channel(&messenger, channel_name, + &flutter::StandardMethodCodec::GetInstance()); + + channel.InvokeMethod("foo", nullptr); + EXPECT_TRUE(messenger.send_called()); + EXPECT_EQ(messenger.last_reply_handler(), nullptr); +} + +TEST(MethodChannelTest, InvokeWithResponse) { + TestBinaryMessenger messenger; + const std::string channel_name("some_channel"); + MethodChannel channel(&messenger, channel_name, + &flutter::StandardMethodCodec::GetInstance()); + + bool received_reply = false; + const std::string reply = "bar"; + auto result_handler = std::make_unique>( + [&received_reply, reply](const EncodableValue* success_value) { + received_reply = true; + EXPECT_EQ(success_value->StringValue(), reply); + }, + nullptr, nullptr); + + channel.InvokeMethod("foo", nullptr, std::move(result_handler)); + EXPECT_TRUE(messenger.send_called()); + ASSERT_NE(messenger.last_reply_handler(), nullptr); + + // Call the underlying reply handler to ensure it's processed correctly. + EncodableValue reply_value(reply); + std::unique_ptr> encoded_reply = + flutter::StandardMethodCodec::GetInstance().EncodeSuccessEnvelope( + &reply_value); + messenger.last_reply_handler()(encoded_reply->data(), encoded_reply->size()); + EXPECT_TRUE(received_reply); +} + +TEST(MethodChannelTest, InvokeNotImplemented) { + TestBinaryMessenger messenger; + const std::string channel_name("some_channel"); + MethodChannel channel(&messenger, channel_name, + &flutter::StandardMethodCodec::GetInstance()); + + bool received_not_implemented = false; + auto result_handler = std::make_unique>( + nullptr, nullptr, + [&received_not_implemented]() { received_not_implemented = true; }); + + channel.InvokeMethod("foo", nullptr, std::move(result_handler)); + EXPECT_EQ(messenger.send_called(), true); + ASSERT_NE(messenger.last_reply_handler(), nullptr); + + // Call the underlying reply handler to ensure it's reported as unimplemented. + messenger.last_reply_handler()(nullptr, 0); + EXPECT_TRUE(received_not_implemented); +} + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/method_result_functions_unittests.cc b/shell/platform/common/cpp/client_wrapper/method_result_functions_unittests.cc new file mode 100644 index 0000000000000..98750dbba244b --- /dev/null +++ b/shell/platform/common/cpp/client_wrapper/method_result_functions_unittests.cc @@ -0,0 +1,66 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace flutter { + +// Tests that unset handlers don't cause crashes. +TEST(MethodChannelTest, NoHandlers) { + MethodResultFunctions result(nullptr, nullptr, nullptr); + result.Success(); + result.Error("error"); + result.NotImplemented(); +} + +// Tests that Success calls through to handler. +TEST(MethodChannelTest, Success) { + bool called = false; + int value = 1; + MethodResultFunctions result( + [&called, value](const int* i) { + called = true; + EXPECT_EQ(*i, value); + }, + nullptr, nullptr); + result.Success(&value); + EXPECT_TRUE(called); +} + +// Tests that Error calls through to handler. +TEST(MethodChannelTest, Error) { + bool called = false; + std::string error_code = "a"; + std::string error_message = "b"; + int error_details = 1; + MethodResultFunctions result( + nullptr, + [&called, error_code, error_message, error_details]( + const std::string& code, const std::string& message, + const int* details) { + called = true; + EXPECT_EQ(code, error_code); + EXPECT_EQ(message, error_message); + EXPECT_EQ(*details, error_details); + }, + nullptr); + result.Error(error_code, error_message, &error_details); + EXPECT_TRUE(called); +} + +// Tests that NotImplemented calls through to handler. +TEST(MethodChannelTest, NotImplemented) { + bool called = false; + MethodResultFunctions result(nullptr, nullptr, + [&called]() { called = true; }); + result.NotImplemented(); + EXPECT_TRUE(called); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc index 92584a93ee903..9527dd73ead3d 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc @@ -26,7 +26,7 @@ void ForwardToHandler(FlutterDesktopMessengerRef messenger, auto* response_handle = message->response_handle; BinaryReply reply_handler = [messenger, response_handle]( const uint8_t* reply, - const size_t reply_size) mutable { + size_t reply_size) mutable { if (!response_handle) { std::cerr << "Error: Response can be set only once. Ignoring " "duplicate response." @@ -65,12 +65,7 @@ class BinaryMessengerImpl : public BinaryMessenger { // |flutter::BinaryMessenger| void Send(const std::string& channel, const uint8_t* message, - const size_t message_size) const override; - - // |flutter::BinaryMessenger| - void Send(const std::string& channel, - const uint8_t* message, - const size_t message_size, + size_t message_size, BinaryReply reply) const override; // |flutter::BinaryMessenger| @@ -88,14 +83,7 @@ class BinaryMessengerImpl : public BinaryMessenger { void BinaryMessengerImpl::Send(const std::string& channel, const uint8_t* message, - const size_t message_size) const { - FlutterDesktopMessengerSend(messenger_, channel.c_str(), message, - message_size); -} - -void BinaryMessengerImpl::Send(const std::string& channel, - const uint8_t* message, - const size_t message_size, + size_t message_size, BinaryReply reply) const { if (reply == nullptr) { FlutterDesktopMessengerSend(messenger_, channel.c_str(), message, diff --git a/shell/platform/common/cpp/client_wrapper/standard_codec.cc b/shell/platform/common/cpp/client_wrapper/standard_codec.cc index d16ce3b54f077..1793174819a62 100644 --- a/shell/platform/common/cpp/client_wrapper/standard_codec.cc +++ b/shell/platform/common/cpp/client_wrapper/standard_codec.cc @@ -285,7 +285,7 @@ StandardMessageCodec::~StandardMessageCodec() = default; std::unique_ptr StandardMessageCodec::DecodeMessageInternal( const uint8_t* binary_message, - const size_t message_size) const { + size_t message_size) const { StandardCodecSerializer serializer; ByteBufferStreamReader stream(binary_message, message_size); return std::make_unique(serializer.ReadValue(&stream)); @@ -311,7 +311,7 @@ const StandardMethodCodec& StandardMethodCodec::GetInstance() { std::unique_ptr> StandardMethodCodec::DecodeMethodCallInternal(const uint8_t* message, - const size_t message_size) const { + size_t message_size) const { StandardCodecSerializer serializer; ByteBufferStreamReader stream(message, message_size); EncodableValue method_name = serializer.ReadValue(&stream); @@ -379,4 +379,31 @@ StandardMethodCodec::EncodeErrorEnvelopeInternal( return encoded; } +bool StandardMethodCodec::DecodeAndProcessResponseEnvelopeInternal( + const uint8_t* response, + size_t response_size, + MethodResult* result) const { + StandardCodecSerializer serializer; + ByteBufferStreamReader stream(response, response_size); + uint8_t flag = stream.ReadByte(); + switch (flag) { + case 0: { + EncodableValue value = serializer.ReadValue(&stream); + result->Success(value.IsNull() ? nullptr : &value); + return true; + } + case 1: { + EncodableValue code = serializer.ReadValue(&stream); + EncodableValue message = serializer.ReadValue(&stream); + EncodableValue details = serializer.ReadValue(&stream); + result->Error(code.StringValue(), + message.IsNull() ? "" : message.StringValue(), + details.IsNull() ? nullptr : &details); + return true; + } + default: + return false; + } +} + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc b/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc index e3f4b15de77a8..4b43b40229038 100644 --- a/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h" #include "flutter/shell/platform/common/cpp/client_wrapper/testing/encodable_value_utils.h" #include "gtest/gtest.h" @@ -60,7 +61,17 @@ TEST(StandardMethodCodec, HandlesSuccessEnvelopesWithNullResult) { ASSERT_NE(encoded.get(), nullptr); std::vector bytes = {0x00, 0x00}; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + [&decoded_successfully](const EncodableValue* result) { + decoded_successfully = true; + EXPECT_EQ(result, nullptr); + }, + nullptr, nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } TEST(StandardMethodCodec, HandlesSuccessEnvelopesWithResult) { @@ -70,7 +81,17 @@ TEST(StandardMethodCodec, HandlesSuccessEnvelopesWithResult) { ASSERT_NE(encoded.get(), nullptr); std::vector bytes = {0x00, 0x03, 0x2a, 0x00, 0x00, 0x00}; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + [&decoded_successfully](const EncodableValue* result) { + decoded_successfully = true; + EXPECT_EQ(result->IntValue(), 42); + }, + nullptr, nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } TEST(StandardMethodCodec, HandlesErrorEnvelopesWithNulls) { @@ -80,7 +101,22 @@ TEST(StandardMethodCodec, HandlesErrorEnvelopesWithNulls) { std::vector bytes = {0x01, 0x07, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x00, 0x00}; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + nullptr, + [&decoded_successfully](const std::string& code, + const std::string& message, + const EncodableValue* details) { + decoded_successfully = true; + EXPECT_EQ(code, "errorCode"); + EXPECT_EQ(message, ""); + EXPECT_EQ(details, nullptr); + }, + nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } TEST(StandardMethodCodec, HandlesErrorEnvelopesWithDetails) { @@ -99,7 +135,24 @@ TEST(StandardMethodCodec, HandlesErrorEnvelopesWithDetails) { 0x0c, 0x02, 0x07, 0x01, 0x61, 0x03, 0x2a, 0x00, 0x00, 0x00, }; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + nullptr, + [&decoded_successfully](const std::string& code, + const std::string& message, + const EncodableValue* details) { + decoded_successfully = true; + EXPECT_EQ(code, "errorCode"); + EXPECT_EQ(message, "something failed"); + EXPECT_TRUE(details->IsList()); + EXPECT_EQ(details->ListValue()[0].StringValue(), "a"); + EXPECT_EQ(details->ListValue()[1].IntValue(), 42); + }, + nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } } // namespace flutter diff --git a/shell/platform/common/cpp/json_method_codec.cc b/shell/platform/common/cpp/json_method_codec.cc index 26f6a2402082b..4a37eb4950d33 100644 --- a/shell/platform/common/cpp/json_method_codec.cc +++ b/shell/platform/common/cpp/json_method_codec.cc @@ -9,9 +9,27 @@ namespace flutter { namespace { + // Keys used in MethodCall encoding. constexpr char kMessageMethodKey[] = "method"; constexpr char kMessageArgumentsKey[] = "args"; + +// Returns a new document containing only |element|, which must be an element +// in |document|. This is a move rather than a copy, so it is efficient but +// destructive to the data in |document|. +std::unique_ptr ExtractElement( + rapidjson::Document* document, + rapidjson::Value* subtree) { + auto extracted = std::make_unique(); + // Pull the subtree up to the root of the document. + document->Swap(*subtree); + // Swap the entire document into |extracted|. Unlike the swap above this moves + // the allocator ownership, so the data won't be deleted when |document| is + // destroyed. + extracted->Swap(*document); + return extracted; +} + } // namespace // static @@ -22,7 +40,7 @@ const JsonMethodCodec& JsonMethodCodec::GetInstance() { std::unique_ptr> JsonMethodCodec::DecodeMethodCallInternal(const uint8_t* message, - const size_t message_size) const { + size_t message_size) const { std::unique_ptr json_message = JsonMessageCodec::GetInstance().DecodeMessage(message, message_size); if (!json_message) { @@ -40,19 +58,7 @@ JsonMethodCodec::DecodeMethodCallInternal(const uint8_t* message, auto arguments_iter = json_message->FindMember(kMessageArgumentsKey); std::unique_ptr arguments; if (arguments_iter != json_message->MemberEnd()) { - // Pull the arguments subtree up to the root of json_message. This is - // destructive to json_message, but the full value is no longer needed, and - // this avoids a subtree copy. - // Note: The static_cast is for compatibility with RapidJSON 1.1; master - // already allows swapping a Document with a Value directly. Once there is - // a new RapidJSON release (at which point clients can be expected to have - // that change in the version they depend on) remove the cast. - static_cast(json_message.get()) - ->Swap(arguments_iter->value); - // Swap it into |arguments|. This moves the allocator ownership, so that - // the data won't be deleted when json_message goes out of scope. - arguments = std::make_unique(); - arguments->Swap(*json_message); + arguments = ExtractElement(json_message.get(), &(arguments_iter->value)); } return std::make_unique>( method_name, std::move(arguments)); @@ -108,4 +114,36 @@ JsonMethodCodec::EncodeErrorEnvelopeInternal( return JsonMessageCodec::GetInstance().EncodeMessage(envelope); } +bool JsonMethodCodec::DecodeAndProcessResponseEnvelopeInternal( + const uint8_t* response, + size_t response_size, + MethodResult* result) const { + std::unique_ptr json_response = + JsonMessageCodec::GetInstance().DecodeMessage(response, response_size); + if (!json_response) { + return false; + } + if (!json_response->IsArray()) { + return false; + } + switch (json_response->Size()) { + case 1: { + std::unique_ptr value = + ExtractElement(json_response.get(), &((*json_response)[0])); + result->Success(value->IsNull() ? nullptr : value.get()); + return true; + } + case 3: { + std::string code = (*json_response)[0].GetString(); + std::string message = (*json_response)[1].GetString(); + std::unique_ptr details = + ExtractElement(json_response.get(), &((*json_response)[2])); + result->Error(code, message, details->IsNull() ? nullptr : details.get()); + return true; + } + default: + return false; + } +} + } // namespace flutter diff --git a/shell/platform/common/cpp/json_method_codec.h b/shell/platform/common/cpp/json_method_codec.h index 597fb6e6d1f16..25e3963fcb03d 100644 --- a/shell/platform/common/cpp/json_method_codec.h +++ b/shell/platform/common/cpp/json_method_codec.h @@ -46,6 +46,12 @@ class JsonMethodCodec : public MethodCodec { const std::string& error_code, const std::string& error_message, const rapidjson::Document* error_details) const override; + + // |flutter::MethodCodec| + bool DecodeAndProcessResponseEnvelopeInternal( + const uint8_t* response, + const size_t response_size, + MethodResult* result) const override; }; } // namespace flutter diff --git a/shell/platform/common/cpp/json_method_codec_unittests.cc b/shell/platform/common/cpp/json_method_codec_unittests.cc index 36bffcff1d104..f9d2ec882fc45 100644 --- a/shell/platform/common/cpp/json_method_codec_unittests.cc +++ b/shell/platform/common/cpp/json_method_codec_unittests.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/common/cpp/json_method_codec.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_result_functions.h" #include "gtest/gtest.h" namespace flutter { @@ -60,7 +61,17 @@ TEST(JsonMethodCodec, HandlesSuccessEnvelopesWithNullResult) { ASSERT_TRUE(encoded); std::vector bytes = {'[', 'n', 'u', 'l', 'l', ']'}; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + [&decoded_successfully](const rapidjson::Document* result) { + decoded_successfully = true; + EXPECT_EQ(result, nullptr); + }, + nullptr, nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } TEST(JsonMethodCodec, HandlesSuccessEnvelopesWithResult) { @@ -71,7 +82,17 @@ TEST(JsonMethodCodec, HandlesSuccessEnvelopesWithResult) { ASSERT_TRUE(encoded); std::vector bytes = {'[', '4', '2', ']'}; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + [&decoded_successfully](const rapidjson::Document* result) { + decoded_successfully = true; + EXPECT_EQ(result->GetInt(), 42); + }, + nullptr, nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } TEST(JsonMethodCodec, HandlesErrorEnvelopesWithNulls) { @@ -83,7 +104,22 @@ TEST(JsonMethodCodec, HandlesErrorEnvelopesWithNulls) { '"', ',', '"', '"', ',', 'n', 'u', 'l', 'l', ']', }; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + nullptr, + [&decoded_successfully](const std::string& code, + const std::string& message, + const rapidjson::Document* details) { + decoded_successfully = true; + EXPECT_EQ(code, "errorCode"); + EXPECT_EQ(message, ""); + EXPECT_EQ(details, nullptr); + }, + nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } TEST(JsonMethodCodec, HandlesErrorEnvelopesWithDetails) { @@ -101,7 +137,24 @@ TEST(JsonMethodCodec, HandlesErrorEnvelopesWithDetails) { 'e', 'd', '"', ',', '[', '"', 'a', '"', ',', '4', '2', ']', ']', }; EXPECT_EQ(*encoded, bytes); - // TODO: Add round-trip check once decoding replies is implemented. + + bool decoded_successfully = false; + MethodResultFunctions result_handler( + nullptr, + [&decoded_successfully](const std::string& code, + const std::string& message, + const rapidjson::Document* details) { + decoded_successfully = true; + EXPECT_EQ(code, "errorCode"); + EXPECT_EQ(message, "something failed"); + EXPECT_TRUE(details->IsArray()); + EXPECT_EQ(std::string((*details)[0].GetString()), "a"); + EXPECT_EQ((*details)[1].GetInt(), 42); + }, + nullptr); + codec.DecodeAndProcessResponseEnvelope(encoded->data(), encoded->size(), + &result_handler); + EXPECT_TRUE(decoded_successfully); } } // namespace flutter From 720645e18d5580722b388a4f6e688ee5978a3941 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 12 Apr 2020 03:55:02 -0400 Subject: [PATCH 363/386] Roll src/third_party/dart 89b0f6726123..23a8788f6a9c (1 commits) (#17659) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 6971b2c22a528..3741ad315aebc 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '89b0f67261232822da1d2cf818b3c2bbe8fa58e2', + 'dart_revision': '23a8788f6a9c453f3786c4330c4876e9d14608ef', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 80ada900a5fca0cf58f4c00b10b3980c70a90c9c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 12 Apr 2020 04:45:02 -0400 Subject: [PATCH 364/386] Roll src/third_party/skia 459ecee2cbdc..6dfc55454671 (2 commits) (#17663) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 3741ad315aebc..1df5dca65aba9 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '459ecee2cbdc2bad7a3c750af64ebff194c73ea5', + 'skia_revision': '6dfc554546710dcd1b52db60fe23031cf8499893', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2477efa18bb12..c9dd3c1c65d33 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 058ee3f539b545b97fc0ec27768fa9ab +Signature: c4a1638e2e0e7c2c9906edea6665c5f6 UNUSED LICENSES: From aef9986cf14aa2e15cc582bfcac6c1b0f54b7a61 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 12 Apr 2020 05:10:02 -0400 Subject: [PATCH 365/386] Roll fuchsia/sdk/core/mac-amd64 from zCKPv... to yCVt4... (#17661) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 1df5dca65aba9..1ccbe395bbe57 100644 --- a/DEPS +++ b/DEPS @@ -538,7 +538,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'zCKPvsueceqBCaNV0iSuGUljmzilg7wYD_NZJPixOiEC' + 'version': 'yCVt4URXUFVrTVe3IekH7R1RW5BT0hG0UD906SB8SkMC' } ], 'condition': 'host_os == "mac"', From cc7d2857a94e919b77f156ecf88b62c9ec42e712 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 12 Apr 2020 17:30:02 -0400 Subject: [PATCH 366/386] Roll src/third_party/dart 23a8788f6a9c..8ef508ba3667 (2 commits) (#17668) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 1ccbe395bbe57..a9ffa034c1386 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '23a8788f6a9c453f3786c4330c4876e9d14608ef', + 'dart_revision': '8ef508ba3667ee817e1eee61770e529ccacec043', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 926f6fcbbc10c0d51a82658cca4555a344363fd1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 12 Apr 2020 21:05:01 -0400 Subject: [PATCH 367/386] Roll fuchsia/sdk/core/mac-amd64 from yCVt4... to G7eYf... (#17671) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index a9ffa034c1386..ef58e33fb3a79 100644 --- a/DEPS +++ b/DEPS @@ -538,7 +538,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'yCVt4URXUFVrTVe3IekH7R1RW5BT0hG0UD906SB8SkMC' + 'version': 'G7eYf_FUdJN8_BqA8wdx1P_YnsRDndhTJ4hvAVd5uT8C' } ], 'condition': 'host_os == "mac"', From d957befe1377cfa14cbc7a859152dba2699fa419 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 02:10:01 -0400 Subject: [PATCH 368/386] Roll src/third_party/skia 6dfc55454671..32c61af49cdb (1 commits) (#17672) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ef58e33fb3a79..af211dc52eb78 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6dfc554546710dcd1b52db60fe23031cf8499893', + 'skia_revision': '32c61af49cdb9d8964b2adf2811564c0f410d3d3', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index c9dd3c1c65d33..b11f78db678bd 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c4a1638e2e0e7c2c9906edea6665c5f6 +Signature: 0759c3c5b1463dc58a3e88f001928fbd UNUSED LICENSES: From 521c1d44312513242c80dacb11ef87f7bacf9930 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 02:45:01 -0400 Subject: [PATCH 369/386] Roll src/third_party/dart 8ef508ba3667..c9710e5059ad (1 commits) (#17673) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index af211dc52eb78..3875ec69ed528 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '8ef508ba3667ee817e1eee61770e529ccacec043', + 'dart_revision': 'c9710e5059ad94b7d4f1db951cc515a8d9d09ead', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 405fe37dcd10816555e95d97d16a2a0447c6ff05 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 05:50:01 -0400 Subject: [PATCH 370/386] Roll src/third_party/skia 32c61af49cdb..4b4efe4d6f1f (3 commits) (#17675) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 3875ec69ed528..133db53eaddac 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '32c61af49cdb9d8964b2adf2811564c0f410d3d3', + 'skia_revision': '4b4efe4d6f1f6de7b9a5e3167991ddcdc20a8df9', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index b11f78db678bd..bfd5ab8d14851 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 0759c3c5b1463dc58a3e88f001928fbd +Signature: ac26501f915d2d73617ec3e00f94a944 UNUSED LICENSES: From 85b9c9cc43bd2d9d7395b5cb001a8fa5620b51cb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 10:00:02 -0400 Subject: [PATCH 371/386] Roll src/third_party/skia 4b4efe4d6f1f..d468a1619a2f (1 commits) (#17677) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 133db53eaddac..8b9f2ca64ce6a 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4b4efe4d6f1f6de7b9a5e3167991ddcdc20a8df9', + 'skia_revision': 'd468a1619a2f3fe7ba0df668e949c0492c2a87f6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index bfd5ab8d14851..f3eb922d871ed 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ac26501f915d2d73617ec3e00f94a944 +Signature: d5e054dd8461b290526b5ec7d32fe7e7 UNUSED LICENSES: From deef2663aca452dcd692c8b1b4ecf5a09239a0fc Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Mon, 13 Apr 2020 09:54:54 -0700 Subject: [PATCH 372/386] Run integration tests on ci (#17598) * changing felt script to fetch flutter * changing the clone_flutter.sh code * running integration tests with felt on cirrus. fetch framework in CI (not in local). * only run cirrus tests on chrome. fix a comma in the flutter command path * adding comments to public flags * use local engine parameter for flutter pub get * change flutter executable used for flutter drive command * fix a cleanup issue. address comments. add toolException. enable web in flutter * address reviwer comments. fix issue with local-engine * address reviwer comments. fix issue with local-engine * using engine/flutter/.dart_tools as clone directory. enabling clone script for local usage * clean flutter repo with felt clean. add a flag to skip cloning the repo. always clone the repo even for local development, unless this flag is set * fixing typos. updating readme for the new flag. * fix directory error * addressing reviewer comments --- lib/web_ui/dev/README.md | 8 ++- lib/web_ui/dev/clean.dart | 9 +++ lib/web_ui/dev/common.dart | 8 +++ lib/web_ui/dev/environment.dart | 35 ++++++++++ lib/web_ui/dev/felt.dart | 1 - lib/web_ui/dev/integration_tests_manager.dart | 68 ++++++++++++------- lib/web_ui/dev/test_runner.dart | 26 +++++-- lib/web_ui/dev/utils.dart | 21 ++++++ tools/clone_flutter.sh | 28 ++++++-- 9 files changed, 165 insertions(+), 39 deletions(-) diff --git a/lib/web_ui/dev/README.md b/lib/web_ui/dev/README.md index c79dc1f3d6c46..dba654e49c54e 100644 --- a/lib/web_ui/dev/README.md +++ b/lib/web_ui/dev/README.md @@ -43,12 +43,18 @@ To run unit tests only: felt test --unit-tests-only ``` -To run integration tests only. For now these tests are only available on Chrome Desktop browsers. +To run integration tests only. For now these tests are only available on Chrome Desktop browsers. These tests will fetch the flutter repository for using `flutter drive` and `flutter pub get` commands. The repository will be synced to the youngest commit older than the engine commit. ``` felt test --integration-tests-only ``` +To skip cloning the flutter repository use the following flag. This flag can save internet bandwidth. However use with caution. Note the tests results will not be consistent with CIs when this flag is set. flutter command should be set in the PATH for this flag to be useful. This flag can also be used to test local Flutter changes. + +``` +felt test --integration-tests-only --use-system-flutter +``` + To run tests on Firefox (this will work only on a Linux device): ``` diff --git a/lib/web_ui/dev/clean.dart b/lib/web_ui/dev/clean.dart index ba2da00c857f6..b3d065fc66534 100644 --- a/lib/web_ui/dev/clean.dart +++ b/lib/web_ui/dev/clean.dart @@ -15,6 +15,11 @@ import 'utils.dart'; class CleanCommand extends Command with ArgUtils { CleanCommand() { argParser + ..addFlag( + 'flutter', + defaultsTo: true, + help: 'Cleans up the .dart_tool directory under engine/src/flutter.', + ) ..addFlag( 'ninja', defaultsTo: false, @@ -27,6 +32,8 @@ class CleanCommand extends Command with ArgUtils { bool get _alsoCleanNinja => boolArg('ninja'); + bool get _alsoCleanFlutterRepo => boolArg('flutter'); + @override String get description => 'Deletes build caches and artifacts.'; @@ -48,6 +55,8 @@ class CleanCommand extends Command with ArgUtils { ...fontFiles, if (_alsoCleanNinja) environment.outDir, + if(_alsoCleanFlutterRepo) + environment.engineDartToolDir, ]; await Future.wait( diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 344316be76511..588fe67eedf6f 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -231,4 +231,12 @@ class DevNull implements StringSink { void writeln([Object obj = ""]) {} } +/// Whether the felt command is running on Cirrus CI. bool get isCirrus => io.Platform.environment['CIRRUS_CI'] == 'true'; + +/// Whether the felt command is running on LUCI. +bool get isLuci => io.Platform.environment['LUCI_CONTEXT'] != null; + +/// Whether the felt command is running on one of the Continuous Integration +/// environements. +bool get isCi => isCirrus || isLuci; diff --git a/lib/web_ui/dev/environment.dart b/lib/web_ui/dev/environment.dart index 3214c8a083b3d..824bded595267 100644 --- a/lib/web_ui/dev/environment.dart +++ b/lib/web_ui/dev/environment.dart @@ -20,6 +20,7 @@ class Environment { factory Environment() { final io.File self = io.File.fromUri(io.Platform.script); final io.Directory engineSrcDir = self.parent.parent.parent.parent.parent; + final io.Directory engineToolsDir = io.Directory(pathlib.join(engineSrcDir.path, 'flutter', 'tools')); final io.Directory outDir = io.Directory(pathlib.join(engineSrcDir.path, 'out')); final io.Directory hostDebugUnoptDir = io.Directory(pathlib.join(outDir.path, 'host_debug_unopt')); final io.Directory dartSdkDir = io.Directory(pathlib.join(hostDebugUnoptDir.path, 'dart-sdk')); @@ -36,6 +37,7 @@ class Environment { self: self, webUiRootDir: webUiRootDir, engineSrcDir: engineSrcDir, + engineToolsDir: engineToolsDir, integrationTestsDir: integrationTestsDir, outDir: outDir, hostDebugUnoptDir: hostDebugUnoptDir, @@ -47,6 +49,7 @@ class Environment { this.self, this.webUiRootDir, this.engineSrcDir, + this.engineToolsDir, this.integrationTestsDir, this.outDir, this.hostDebugUnoptDir, @@ -62,6 +65,9 @@ class Environment { /// Path to the engine's "src" directory. final io.Directory engineSrcDir; + /// Path to the engine's "tools" directory. + final io.Directory engineToolsDir; + /// Path to the web integration tests. final io.Directory integrationTestsDir; @@ -112,6 +118,16 @@ class Environment { '.dart_tool', )); + /// Path to the ".dart_tool" directory living under `engine/src/flutter`. + /// + /// This is a designated area for tool downloads which can be used by + /// multiple platforms. For exampe: Flutter repo for e2e tests. + io.Directory get engineDartToolDir => io.Directory(pathlib.join( + engineSrcDir.path, + 'flutter', + '.dart_tool', + )); + /// Path to the "dev" directory containing engine developer tools and /// configuration files. io.Directory get webUiDevDir => io.Directory(pathlib.join( @@ -124,4 +140,23 @@ class Environment { webUiDartToolDir.path, 'goldens', )); + + /// Path to the script that clones the Flutter repo. + io.File get cloneFlutterScript => io.File(pathlib.join( + engineToolsDir.path, + 'clone_flutter.sh', + )); + + /// Path to flutter. + /// + /// For example, this can be used to run `flutter pub get`. + /// + /// Only use [cloneFlutterScript] to clone flutter to the engine build. + io.File get flutterCommand => io.File(pathlib.join( + engineDartToolDir.path, + 'flutter', + 'bin', + 'flutter', + )); + } diff --git a/lib/web_ui/dev/felt.dart b/lib/web_ui/dev/felt.dart index 32f3619dc7014..6366b6152991a 100644 --- a/lib/web_ui/dev/felt.dart +++ b/lib/web_ui/dev/felt.dart @@ -37,7 +37,6 @@ void main(List args) async { final bool result = (await runner.run(args)) as bool; if (result == false) { print('Sub-command returned false: `${args.join(' ')}`'); - await cleanup(); exitCode = 1; } } on UsageException catch (e) { diff --git a/lib/web_ui/dev/integration_tests_manager.dart b/lib/web_ui/dev/integration_tests_manager.dart index 811e876664541..ec3595dedf268 100644 --- a/lib/web_ui/dev/integration_tests_manager.dart +++ b/lib/web_ui/dev/integration_tests_manager.dart @@ -7,8 +7,8 @@ import 'package:path/path.dart' as pathlib; import 'package:web_driver_installer/chrome_driver_installer.dart'; import 'chrome_installer.dart'; -import 'common.dart'; import 'environment.dart'; +import 'exceptions.dart'; import 'utils.dart'; class IntegrationTestsManager { @@ -29,7 +29,9 @@ class IntegrationTestsManager { /// tests shutdown. final io.Directory _drivers; - IntegrationTestsManager(this._browser) + final bool _useSystemFlutter; + + IntegrationTestsManager(this._browser, this._useSystemFlutter) : this._browserDriverDir = io.Directory(pathlib.join( environment.webUiDartToolDir.path, 'drivers', _browser)), this._drivers = io.Directory( @@ -46,37 +48,49 @@ class IntegrationTestsManager { } } - Future _runPubGet(String workingDirectory) async { - final String executable = isCirrus ? environment.pubExecutable : 'flutter'; - final List arguments = isCirrus - ? [ - 'get', - ] - : [ - 'pub', - 'get', - ]; + Future _runPubGet(String workingDirectory) async { + if (!_useSystemFlutter) { + await _cloneFlutterRepo(); + await _enableWeb(workingDirectory); + } + await runFlutter(workingDirectory, ['pub', 'get'], + useSystemFlutter: _useSystemFlutter); + } + + /// Clone flutter repository, use the youngest commit older than the engine + /// commit. + /// + /// Use engine/src/flutter/.dart_tools to clone the Flutter repo. + /// TODO(nurhan): Use git pull instead if repo exists. + Future _cloneFlutterRepo() async { + // Delete directory if exists. + if (environment.engineDartToolDir.existsSync()) { + environment.engineDartToolDir.deleteSync(); + } + environment.engineDartToolDir.createSync(); + final int exitCode = await runProcess( - executable, - arguments, - workingDirectory: workingDirectory, + environment.cloneFlutterScript.path, + [ + environment.engineDartToolDir.path, + ], + workingDirectory: environment.webUiRootDir.path, ); if (exitCode != 0) { - io.stderr.writeln( - 'ERROR: Failed to run pub get. Exited with exit code $exitCode'); - return false; - } else { - return true; + throw ToolException('ERROR: Failed to clone flutter repo. Exited with ' + 'exit code $exitCode'); } } + Future _enableWeb(String workingDirectory) async { + await runFlutter(workingDirectory, ['config', '--enable-web'], + useSystemFlutter: _useSystemFlutter); + } + void _runDriver() async { - startProcess( - './chromedriver/chromedriver', - ['--port=4444'], - workingDirectory: io.Directory.current.path - ); + startProcess('./chromedriver/chromedriver', ['--port=4444'], + workingDirectory: io.Directory.current.path); print('INFO: Driver started'); } @@ -175,8 +189,10 @@ class IntegrationTestsManager { Future _runTestsInProfileMode( io.Directory directory, String testName) async { + final String executable = + _useSystemFlutter ? 'flutter' : environment.flutterCommand.path; final int exitCode = await runProcess( - 'flutter', + executable, [ 'drive', '--target=test_driver/${testName}', diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 5b62a064d7b8d..598f85d9fb717 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -15,11 +15,11 @@ import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_im import 'package:test_core/src/executable.dart' as test; // ignore: implementation_imports +import 'environment.dart'; import 'exceptions.dart'; import 'integration_tests_manager.dart'; import 'supported_browsers.dart'; import 'test_platform.dart'; -import 'environment.dart'; import 'utils.dart'; /// The type of tests requested by the tool user. @@ -56,6 +56,18 @@ class TestCommand extends Command with ArgUtils { 'at the same time. If this flag is set, only run the integration ' 'tests.', ) + ..addFlag('use-system-flutter', + defaultsTo: false, + help: 'integration tests are using flutter repository for various tasks' + ', such as flutter drive, flutter pub get. If this flag is set, felt ' + 'will use flutter command without cloning the repository. This flag ' + 'can save internet bandwidth. However use with caution. Note that ' + 'since flutter repo is always synced to youngest commit older than ' + 'the engine commit for the tests running in CI, the tests results ' + 'won\'t be consistent with CIs when this flag is set. flutter ' + 'command should be set in the PATH for this flag to be useful.' + 'This flag can also be used to test local Flutter changes.' + ) ..addFlag( 'update-screenshot-goldens', defaultsTo: false, @@ -117,7 +129,8 @@ class TestCommand extends Command with ArgUtils { return runIntegrationTests(); case TestTypesRequested.all: // TODO(nurhan): https://github.com/flutter/flutter/issues/53322 - if (runAllTests) { + // TODO(nurhan): Expand browser matrix for felt integration tests. + if (runAllTests && isChrome) { bool integrationTestResult = await runIntegrationTests(); bool unitTestResult = await runUnitTests(); if (integrationTestResult != unitTestResult) { @@ -134,11 +147,11 @@ class TestCommand extends Command with ArgUtils { Future runIntegrationTests() async { // TODO(nurhan): https://github.com/flutter/flutter/issues/52983 - if (io.Platform.environment['LUCI_CONTEXT'] != null || isCirrus) { + if (io.Platform.environment['LUCI_CONTEXT'] != null) { return true; } - return IntegrationTestsManager(browser).runTests(); + return IntegrationTestsManager(browser, useSystemFlutter).runTests(); } Future runUnitTests() async { @@ -205,6 +218,11 @@ class TestCommand extends Command with ArgUtils { /// Whether [browser] is set to "chrome". bool get isChrome => browser == 'chrome'; + /// Use system flutter instead of cloning the repository. + /// + /// Read the flag help for more details. Uses PATH to locate flutter. + bool get useSystemFlutter => boolArg('use-system-flutter'); + /// When running screenshot tests writes them to the file system into /// ".dart_tool/goldens". bool get doUpdateScreenshotGoldens => boolArg('update-screenshot-goldens'); diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index bf3475e8874fc..2ccdb04c5f049 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -11,6 +11,7 @@ import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; import 'environment.dart'; +import 'exceptions.dart'; class FilePath { FilePath.fromCwd(String relativePath) @@ -107,6 +108,26 @@ Future evalProcess( return result.stdout as String; } +Future runFlutter( + String workingDirectory, + List arguments, { + bool useSystemFlutter = false, +}) async { + final String executable = + useSystemFlutter ? 'flutter' : environment.flutterCommand.path; + arguments.add('--local-engine=host_debug_unopt'); + final int exitCode = await runProcess( + executable, + arguments, + workingDirectory: workingDirectory, + ); + + if (exitCode != 0) { + throw ToolException('ERROR: Failed to run $executable with ' + 'arguments ${arguments.toString()}. Exited with exit code $exitCode'); + } +} + @immutable class ProcessException implements Exception { ProcessException({ diff --git a/tools/clone_flutter.sh b/tools/clone_flutter.sh index cb9f4916bc965..48debdf44475c 100755 --- a/tools/clone_flutter.sh +++ b/tools/clone_flutter.sh @@ -4,13 +4,12 @@ set -x if [[ "$CIRRUS_CI" = false || -z $CIRRUS_CI ]] then - echo "This script is aimed to be run on CI environments. Do not run locally." - exit 1 + echo "Cloning Flutter repo to local machine." fi if [[ -z $ENGINE_PATH ]] then - echo "Engine path should be set to run the script." + echo "Please set ENGINE_PATH environment variable." exit 1 fi @@ -33,10 +32,25 @@ fi LATEST_COMMIT_TIME_ENGINE=`git log -1 --date=local --format="%cd"` echo "Latest commit time on engine found as $LATEST_COMMIT_TIME_ENGINE" -# Do rest of the task in the root directory -cd ~ -mkdir -p $FRAMEWORK_PATH -cd $FRAMEWORK_PATH +# Check if there is an argument added for repo location. +# If not use the location that should be set by Cirrus/LUCI. +FLUTTER_CLONE_REPO_PATH=$1 + +if [[ -z $FLUTTER_CLONE_REPO_PATH ]] +then + if [[ -z $FRAMEWORK_PATH ]] + then + echo "Framework path should be set to run the script." + exit 1 + fi + # Do rest of the task in the root directory + cd ~ + mkdir -p $FRAMEWORK_PATH + cd $FRAMEWORK_PATH +else + cd $FLUTTER_CLONE_REPO_PATH +fi + # Clone the Flutter Framework. git clone https://github.com/flutter/flutter.git cd flutter From b9059b55d7925f34e4c5d55a6afba14e3f1d9ba4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 14:15:03 -0400 Subject: [PATCH 373/386] Roll src/third_party/dart c9710e5059ad..2aecf30d2b2e (1 commits) (#17680) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 8b9f2ca64ce6a..5d4df0027fe06 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'c9710e5059ad94b7d4f1db951cc515a8d9d09ead', + 'dart_revision': '2aecf30d2b2e8e5bad0da003d0411c9a8489363a', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From b241ec1d46b8d8a5c4178e7ab880b8369f904035 Mon Sep 17 00:00:00 2001 From: George Wright Date: Thu, 26 Mar 2020 12:17:44 -0700 Subject: [PATCH 374/386] Enable required extension --- shell/common/shell_test_platform_view_vulkan.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 4c21c2dfba36e..3ed4193aa2584 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -60,7 +60,9 @@ ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( } // Create the application instance. - std::vector extensions = {}; + std::vector extensions = { + VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME, + }; application_ = std::make_unique( *vk_, "FlutterTest", std::move(extensions)); From 61ec840edd01e0c7e2be505f4f5d7fefb3c448c3 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 13 Apr 2020 12:34:54 -0700 Subject: [PATCH 375/386] Made it so unit tests can be written against all ios engine code. (#17624) --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/darwin/ios/BUILD.gn | 184 ++++++++++++++--- .../Source/FlutterViewControllerTest.mm | 29 ++- .../framework/Source/SemanticsObjectTest.mm | 41 ++++ .../IosUnitTests.xcodeproj/project.pbxproj | 190 ++---------------- testing/ios/IosUnitTests/README.md | 22 ++ .../ios/IosUnitTests/build_and_run_tests.sh | 19 +- 7 files changed, 271 insertions(+), 215 deletions(-) create mode 100644 shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm create mode 100644 testing/ios/IosUnitTests/README.md diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 0a686a8a090e9..efcc72ebf43e9 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -881,6 +881,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewCon FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index d9be6e2a9f11e..8fd88147c7e64 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -37,15 +37,12 @@ _flutter_framework_headers = [ _flutter_framework_headers_copy_dir = "$_flutter_framework_dir/Headers" -shared_library("create_flutter_framework_dylib") { - visibility = [ ":*" ] - - output_name = "Flutter" - - ldflags = [ "-Wl,-install_name,@rpath/Flutter.framework/Flutter" ] - - public = _flutter_framework_headers +# TODO(54502): move this variable into //build/config/ios/ios_sdk.gni +# Version of iOS that we're targeting for tests. +ios_testing_deployment_target = "13.0" +source_set("flutter_framework_source") { + visibility = [ ":*" ] cflags_objc = flutter_cflags_objc cflags_objcc = flutter_cflags_objcc @@ -112,6 +109,21 @@ shared_library("create_flutter_framework_dylib") { sources += _flutter_framework_headers + defines = [ "FLUTTER_FRAMEWORK=1" ] + + if (shell_enable_metal) { + defines += [ "FLUTTER_SHELL_ENABLE_METAL=1" ] + + sources += [ + "ios_context_metal.h", + "ios_context_metal.mm", + "ios_external_texture_metal.h", + "ios_external_texture_metal.mm", + "ios_surface_metal.h", + "ios_surface_metal.mm", + ] + } + deps = [ ":ios_gpu_configuration", "//flutter/common", @@ -128,21 +140,6 @@ shared_library("create_flutter_framework_dylib") { public_configs = [ "//flutter:config" ] - defines = [ "FLUTTER_FRAMEWORK=1" ] - - if (shell_enable_metal) { - defines += [ "FLUTTER_SHELL_ENABLE_METAL=1" ] - - sources += [ - "ios_context_metal.h", - "ios_context_metal.mm", - "ios_external_texture_metal.h", - "ios_external_texture_metal.mm", - "ios_surface_metal.h", - "ios_surface_metal.mm", - ] - } - libs = [ "CoreMedia.framework", "CoreVideo.framework", @@ -153,6 +150,146 @@ shared_library("create_flutter_framework_dylib") { ] } +ocmock_path = "../../../../../third_party/ocmock/Source" + +# TODO(54503): Clone the OCMock repository so we can add a BUILD.gn to it. +static_library("ocmock") { + configs -= [ "//build/config/compiler:chromium_code" ] + cflags = [ + "-fvisibility=default", + "-mios-simulator-version-min=$ios_testing_deployment_target", + "-Wno-misleading-indentation", + ] + sources = [ + "$ocmock_path/OCMock/NSInvocation+OCMAdditions.h", + "$ocmock_path/OCMock/NSInvocation+OCMAdditions.m", + "$ocmock_path/OCMock/NSMethodSignature+OCMAdditions.h", + "$ocmock_path/OCMock/NSMethodSignature+OCMAdditions.m", + "$ocmock_path/OCMock/NSNotificationCenter+OCMAdditions.h", + "$ocmock_path/OCMock/NSNotificationCenter+OCMAdditions.m", + "$ocmock_path/OCMock/NSObject+OCMAdditions.h", + "$ocmock_path/OCMock/NSObject+OCMAdditions.m", + "$ocmock_path/OCMock/NSValue+OCMAdditions.h", + "$ocmock_path/OCMock/NSValue+OCMAdditions.m", + "$ocmock_path/OCMock/OCClassMockObject.h", + "$ocmock_path/OCMock/OCClassMockObject.m", + "$ocmock_path/OCMock/OCMArg.h", + "$ocmock_path/OCMock/OCMArg.m", + "$ocmock_path/OCMock/OCMArgAction.h", + "$ocmock_path/OCMock/OCMArgAction.m", + "$ocmock_path/OCMock/OCMBlockArgCaller.h", + "$ocmock_path/OCMock/OCMBlockArgCaller.m", + "$ocmock_path/OCMock/OCMBlockCaller.h", + "$ocmock_path/OCMock/OCMBlockCaller.m", + "$ocmock_path/OCMock/OCMBoxedReturnValueProvider.h", + "$ocmock_path/OCMock/OCMBoxedReturnValueProvider.m", + "$ocmock_path/OCMock/OCMConstraint.h", + "$ocmock_path/OCMock/OCMConstraint.m", + "$ocmock_path/OCMock/OCMExceptionReturnValueProvider.h", + "$ocmock_path/OCMock/OCMExceptionReturnValueProvider.m", + "$ocmock_path/OCMock/OCMExpectationRecorder.h", + "$ocmock_path/OCMock/OCMExpectationRecorder.m", + "$ocmock_path/OCMock/OCMFunctions.h", + "$ocmock_path/OCMock/OCMFunctions.m", + "$ocmock_path/OCMock/OCMFunctionsPrivate.h", + "$ocmock_path/OCMock/OCMIndirectReturnValueProvider.h", + "$ocmock_path/OCMock/OCMIndirectReturnValueProvider.m", + "$ocmock_path/OCMock/OCMInvocationExpectation.h", + "$ocmock_path/OCMock/OCMInvocationExpectation.m", + "$ocmock_path/OCMock/OCMInvocationMatcher.h", + "$ocmock_path/OCMock/OCMInvocationMatcher.m", + "$ocmock_path/OCMock/OCMInvocationStub.h", + "$ocmock_path/OCMock/OCMInvocationStub.m", + "$ocmock_path/OCMock/OCMLocation.h", + "$ocmock_path/OCMock/OCMLocation.m", + "$ocmock_path/OCMock/OCMMacroState.h", + "$ocmock_path/OCMock/OCMMacroState.m", + "$ocmock_path/OCMock/OCMNotificationPoster.h", + "$ocmock_path/OCMock/OCMNotificationPoster.m", + "$ocmock_path/OCMock/OCMObserverRecorder.h", + "$ocmock_path/OCMock/OCMObserverRecorder.m", + "$ocmock_path/OCMock/OCMPassByRefSetter.h", + "$ocmock_path/OCMock/OCMPassByRefSetter.m", + "$ocmock_path/OCMock/OCMRealObjectForwarder.h", + "$ocmock_path/OCMock/OCMRealObjectForwarder.m", + "$ocmock_path/OCMock/OCMRecorder.h", + "$ocmock_path/OCMock/OCMRecorder.m", + "$ocmock_path/OCMock/OCMReturnValueProvider.h", + "$ocmock_path/OCMock/OCMReturnValueProvider.m", + "$ocmock_path/OCMock/OCMStubRecorder.h", + "$ocmock_path/OCMock/OCMStubRecorder.m", + "$ocmock_path/OCMock/OCMVerifier.h", + "$ocmock_path/OCMock/OCMVerifier.m", + "$ocmock_path/OCMock/OCMock.h", + "$ocmock_path/OCMock/OCMockObject.h", + "$ocmock_path/OCMock/OCMockObject.m", + "$ocmock_path/OCMock/OCObserverMockObject.h", + "$ocmock_path/OCMock/OCObserverMockObject.m", + "$ocmock_path/OCMock/OCPartialMockObject.h", + "$ocmock_path/OCMock/OCPartialMockObject.m", + "$ocmock_path/OCMock/OCProtocolMockObject.h", + "$ocmock_path/OCMock/OCProtocolMockObject.m", + ] + include_dirs = [ "$ocmock_path" ] +} + +ios_test_flutter_path = rebase_path("$root_out_dir/libios_test_flutter.dylib") +platform_frameworks_path = "$ios_sdk_path/../../Library/Frameworks/" + +# NOTE: This currently only supports simulator targets because of the install_name. +# TODO(54504): Switch the install_name and make the test runner copy the dynamic +# library into the testing bundle. +shared_library("ios_test_flutter") { + visibility = [ ":*" ] + cflags = [ + "-fvisibility=default", + "-F$platform_frameworks_path", + "-fobjc-arc", + "-mios-simulator-version-min=$ios_testing_deployment_target", + ] + ldflags = [ + "-F$platform_frameworks_path", + "-Wl,-framework,XCTest", + "-Wl,-install_name,$ios_test_flutter_path", + ] + configs -= [ + "//build/config/gcc:symbol_visibility_hidden", + "//build/config:symbol_visibility_hidden", + ] + sources = [ + "framework/Source/FlutterBinaryMessengerRelayTest.mm", + "framework/Source/FlutterEngineTest.mm", + "framework/Source/FlutterPluginAppLifeCycleDelegateTest.m", + "framework/Source/FlutterTextInputPluginTest.m", + "framework/Source/FlutterViewControllerTest.mm", + "framework/Source/SemanticsObjectTest.mm", + ] + deps = [ + ":flutter_framework_source", + ":ocmock", + "//flutter/shell/platform/darwin/common:framework_shared", + "//third_party/skia", + ] + include_dirs = [ "$ocmock_path" ] + public_configs = [ "//flutter:config" ] +} + +shared_library("create_flutter_framework_dylib") { + visibility = [ ":*" ] + + output_name = "Flutter" + + ldflags = [ "-Wl,-install_name,@rpath/Flutter.framework/Flutter" ] + + public = _flutter_framework_headers + + deps = [ + ":flutter_framework_source", + ] + + public_configs = [ "//flutter:config" ] +} + copy("copy_dylib") { visibility = [ ":*" ] @@ -257,6 +394,7 @@ test_fixtures("flutter_tests_fixtures") { fixtures = [] } +# Note: This currently isn't used, it might be removed. ios_app("FlutterTests") { testonly = true diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index d851616720905..37f3d6068cdc3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -122,7 +122,9 @@ - (void)testItReportsPlatformBrightnessWhenViewWillAppear { } - (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt { - if (!@available(iOS 13, *)) { + if (@available(iOS 13, *)) { + // noop + } else { return; } @@ -171,7 +173,9 @@ - (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterface #pragma mark - Platform Contrast - (void)testItReportsNormalPlatformContrastByDefault { - if (!@available(iOS 13, *)) { + if (@available(iOS 13, *)) { + // noop + } else { return; } @@ -199,7 +203,9 @@ - (void)testItReportsNormalPlatformContrastByDefault { } - (void)testItReportsPlatformContrastWhenViewWillAppear { - if (!@available(iOS 13, *)) { + if (@available(iOS 13, *)) { + // noop + } else { return; } @@ -227,7 +233,9 @@ - (void)testItReportsPlatformContrastWhenViewWillAppear { } - (void)testItReportsHighContrastWhenTraitCollectionRequestsIt { - if (!@available(iOS 13, *)) { + if (@available(iOS 13, *)) { + // noop + } else { return; } @@ -453,13 +461,12 @@ - (void)testWillDeallocNotification { FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine nibName:nil bundle:nil]; - id observer = - [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification* _Nonnull note) { - [expectation fulfill]; - }]; + [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* _Nonnull note) { + [expectation fulfill]; + }]; realVC = nil; } [self waitForExpectations:@[ expectation ] timeout:1.0]; diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm new file mode 100644 index 0000000000000..b067ee7920fc1 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm @@ -0,0 +1,41 @@ +#import +#import + +#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" + +FLUTTER_ASSERT_ARC + +namespace flutter { +namespace { +class MockAccessibilityBridge : public AccessibilityBridgeIos { + public: + MockAccessibilityBridge() { view_ = [[UIView alloc] init]; } + UIView* view() const override { return view_; } + UIView* textInputView() override { return nil; } + void DispatchSemanticsAction(int32_t id, SemanticsAction action) override {} + void DispatchSemanticsAction(int32_t id, + SemanticsAction action, + std::vector args) override {} + FlutterPlatformViewsController* GetPlatformViewsController() const override { return nil; } + + private: + UIView* view_; +}; +} // namespace +} // namespace flutter + +@interface SemanticsObjectTest : XCTestCase +@end + +@implementation SemanticsObjectTest + +- (void)testCreate { + fml::WeakPtrFactory factory( + new flutter::MockAccessibilityBridge()); + fml::WeakPtr bridge = factory.GetWeakPtr(); + SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0]; + XCTAssertNotNil(object); +} + +@end diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index 1ed8dfcd26acc..950e9dbdb03f3 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -7,20 +7,14 @@ objects = { /* Begin PBXBuildFile section */ - 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 0D1CE5D8233430F400E5D880 /* FlutterChannelsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1CE5D7233430F400E5D880 /* FlutterChannelsTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; - 0D4C3FB022DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; - 0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 0D6AB6B622BB05E100EEE540 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6B522BB05E100EEE540 /* AppDelegate.m */; }; 0D6AB6B922BB05E100EEE540 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6B822BB05E100EEE540 /* ViewController.m */; }; 0D6AB6BC22BB05E100EEE540 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB6BA22BB05E100EEE540 /* Main.storyboard */; }; 0D6AB6BE22BB05E200EEE540 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB6BD22BB05E200EEE540 /* Assets.xcassets */; }; 0D6AB6C122BB05E200EEE540 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB6BF22BB05E200EEE540 /* LaunchScreen.storyboard */; }; 0D6AB6C422BB05E200EEE540 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6C322BB05E200EEE540 /* main.m */; }; - 0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; - 0D6AB72C22BC339F00EEE540 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D6AB72522BC336100EEE540 /* libOCMock.a */; }; 0D6AB73F22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */; }; - 3D8AF6182384C5420033B95F /* FlutterTextInputPluginTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -31,55 +25,10 @@ remoteGlobalIDString = 0D6AB6B022BB05E100EEE540; remoteInfo = IosUnitTests; }; - 0D6AB72022BC336100EEE540 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 030EF0A814632FD000B04273; - remoteInfo = OCMock; - }; - 0D6AB72222BC336100EEE540 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 03565A3118F0566E003AE91E; - remoteInfo = OCMockTests; - }; - 0D6AB72422BC336100EEE540 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 030EF0DC14632FF700B04273; - remoteInfo = OCMockLib; - }; - 0D6AB72622BC336100EEE540 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = D31108AD1828DB8700737925; - remoteInfo = OCMockLibTests; - }; - 0D6AB72822BC336100EEE540 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = F0B950F11B0080BE00942C38; - remoteInfo = "OCMock iOS"; - }; - 0D6AB72A22BC336100EEE540 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 817EB1621BD765130047E85A; - remoteInfo = "OCMock tvOS"; - }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterViewControllerTest.mm; sourceTree = ""; }; 0D1CE5D7233430F400E5D880 /* FlutterChannelsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterChannelsTest.m; sourceTree = ""; }; - 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterPluginAppLifeCycleDelegateTest.m; sourceTree = ""; }; - 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterBinaryMessengerRelayTest.mm; sourceTree = ""; }; 0D6AB6B122BB05E100EEE540 /* IosUnitTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IosUnitTests.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0D6AB6B422BB05E100EEE540 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 0D6AB6B522BB05E100EEE540 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -92,10 +41,7 @@ 0D6AB6C322BB05E200EEE540 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 0D6AB6C922BB05E200EEE540 /* IosUnitTestsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IosUnitTestsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 0D6AB6CF22BB05E200EEE540 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterEngineTest.mm; sourceTree = ""; }; - 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OCMock.xcodeproj; path = ../../../../../third_party/ocmock/Source/OCMock.xcodeproj; sourceTree = ""; }; 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = ""; }; - 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterTextInputPluginTest.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -110,7 +56,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0D6AB72C22BC339F00EEE540 /* libOCMock.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -165,27 +110,12 @@ isa = PBXGroup; children = ( 0D1CE5D62334309900E5D880 /* Source-Common */, - 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */, - 0D6AB6E622BB409F00EEE540 /* Source */, 0D6AB6CF22BB05E200EEE540 /* Info.plist */, 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */, ); path = Tests; sourceTree = ""; }; - 0D6AB6E622BB409F00EEE540 /* Source */ = { - isa = PBXGroup; - children = ( - 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */, - 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */, - 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */, - 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.mm */, - 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */, - ); - name = Source; - path = ../../../../shell/platform/darwin/ios/framework/Source; - sourceTree = ""; - }; 0D6AB6FC22BC1BC300EEE540 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -193,19 +123,6 @@ name = Frameworks; sourceTree = ""; }; - 0D6AB71822BC336100EEE540 /* Products */ = { - isa = PBXGroup; - children = ( - 0D6AB72122BC336100EEE540 /* OCMock.framework */, - 0D6AB72322BC336100EEE540 /* OCMockTests.xctest */, - 0D6AB72522BC336100EEE540 /* libOCMock.a */, - 0D6AB72722BC336100EEE540 /* OCMockLibTests.xctest */, - 0D6AB72922BC336100EEE540 /* OCMock.framework */, - 0D6AB72B22BC336100EEE540 /* OCMock.framework */, - ); - name = Products; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -233,7 +150,6 @@ 0D6AB6C522BB05E200EEE540 /* Sources */, 0D6AB6C622BB05E200EEE540 /* Frameworks */, 0D6AB6C722BB05E200EEE540 /* Resources */, - 0D6AB6E122BB122F00EEE540 /* ShellScript */, ); buildRules = ( ); @@ -274,12 +190,6 @@ mainGroup = 0D6AB6A822BB05E100EEE540; productRefGroup = 0D6AB6B222BB05E100EEE540 /* Products */; projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 0D6AB71822BC336100EEE540 /* Products */; - ProjectRef = 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */; - }, - ); projectRoot = ""; targets = ( 0D6AB6B022BB05E100EEE540 /* IosUnitTests */, @@ -288,51 +198,6 @@ }; /* End PBXProject section */ -/* Begin PBXReferenceProxy section */ - 0D6AB72122BC336100EEE540 /* OCMock.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = OCMock.framework; - remoteRef = 0D6AB72022BC336100EEE540 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 0D6AB72322BC336100EEE540 /* OCMockTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = OCMockTests.xctest; - remoteRef = 0D6AB72222BC336100EEE540 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 0D6AB72522BC336100EEE540 /* libOCMock.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libOCMock.a; - remoteRef = 0D6AB72422BC336100EEE540 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 0D6AB72722BC336100EEE540 /* OCMockLibTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = OCMockLibTests.xctest; - remoteRef = 0D6AB72622BC336100EEE540 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 0D6AB72922BC336100EEE540 /* OCMock.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = OCMock.framework; - remoteRef = 0D6AB72822BC336100EEE540 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 0D6AB72B22BC336100EEE540 /* OCMock.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = OCMock.framework; - remoteRef = 0D6AB72A22BC336100EEE540 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - /* Begin PBXResourcesBuildPhase section */ 0D6AB6AF22BB05E100EEE540 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -354,26 +219,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 0D6AB6E122BB122F00EEE540 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = "/bin/sh -x"; - shellScript = "TARGET_FRAMEWORKS_PATH=\"$TARGET_BUILD_DIR/../Frameworks\"\nFRAMEWORK_PATH=$PROJECT_DIR/../../../../out/$FLUTTER_ENGINE/Flutter.framework\nrsync -avzh \"$FRAMEWORK_PATH\" \"$TARGET_FRAMEWORKS_PATH\"\n"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 0D6AB6AD22BB05E100EEE540 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -389,12 +234,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */, - 3D8AF6182384C5420033B95F /* FlutterTextInputPluginTest.m in Sources */, - 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.mm in Sources */, 0D1CE5D8233430F400E5D880 /* FlutterChannelsTest.m in Sources */, - 0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */, - 0D4C3FB022DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -589,7 +429,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_OBJC_ARC = NO; CODE_SIGN_STYLE = Automatic; - FRAMEWORK_SEARCH_PATHS = ../../../../out/$FLUTTER_ENGINE; + FRAMEWORK_SEARCH_PATHS = ""; HEADER_SEARCH_PATHS = ( ../../../.., ../../../../flutter/shell/platform/darwin/common/framework/Headers, @@ -601,20 +441,17 @@ ../../../../third_party/icu/source/common, ); INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = ""; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Tests", + ../../../../out/$FLUTTER_ENGINE, + ../../../../out/$FLUTTER_ENGINE/obj/flutter/shell/platform/darwin/ios/, ); OTHER_LDFLAGS = ( - "-framework", - Flutter, - "-lOCMock", + "-locmock", "-ObjC", + "-lios_test_flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.google.flutter.IosUnitTestsTests; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -633,7 +470,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_ENABLE_OBJC_ARC = NO; CODE_SIGN_STYLE = Automatic; - FRAMEWORK_SEARCH_PATHS = ../../../../out/$FLUTTER_ENGINE; + FRAMEWORK_SEARCH_PATHS = ""; HEADER_SEARCH_PATHS = ( ../../../.., ../../../../flutter/shell/platform/darwin/common/framework/Headers, @@ -645,20 +482,17 @@ ../../../../third_party/icu/source/common, ); INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + LD_RUNPATH_SEARCH_PATHS = ""; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Tests", + ../../../../out/$FLUTTER_ENGINE, + ../../../../out/$FLUTTER_ENGINE/obj/flutter/shell/platform/darwin/ios/, ); OTHER_LDFLAGS = ( - "-framework", - Flutter, - "-lOCMock", + "-locmock", "-ObjC", + "-lios_test_flutter", ); PRODUCT_BUNDLE_IDENTIFIER = com.google.flutter.IosUnitTestsTests; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/testing/ios/IosUnitTests/README.md b/testing/ios/IosUnitTests/README.md new file mode 100644 index 0000000000000..4f9add7806c32 --- /dev/null +++ b/testing/ios/IosUnitTests/README.md @@ -0,0 +1,22 @@ +# iOS Unit Tests + +These are the unit tests for iOS engine. They can be executed locally and are +also run in LUCI builds. + +## Running Tests + +```sh +./flutter/tools/gn --ios --simulator --unoptimized +cd flutter/testing/ios/IosUnitTests +./build_and_run_tests.sh +``` + +After the `ios_flutter_test` target is built you can also run the tests inside +of xcode with `IosUnitTests.xcodeproj`. + +## Adding Tests + +When you add a new unit test file, also add a reference to that file in +shell/platform/darwin/ios/BUILD.gn, under the `sources` list of the +`ios_flutter_test` target. Once it's there, it will execute with the other +tests. diff --git a/testing/ios/IosUnitTests/build_and_run_tests.sh b/testing/ios/IosUnitTests/build_and_run_tests.sh index 076844bd202ea..0c586192d9490 100755 --- a/testing/ios/IosUnitTests/build_and_run_tests.sh +++ b/testing/ios/IosUnitTests/build_and_run_tests.sh @@ -1,7 +1,20 @@ #!/bin/sh + +FLUTTER_ENGINE=ios_debug_sim_unopt + +if [ $# -eq 1 ]; then + FLUTTER_ENGINE=$1 +fi + pushd $PWD cd ../../../.. -./flutter/tools/gn --ios --simulator --unoptimized -ninja -j 100 -C out/ios_debug_sim_unopt + +if [ ! -d "out/$FLUTTER_ENGINE" ]; then + echo "You must GN to generate out/$FLUTTER_ENGINE" + echo "example: ./flutter/tools/gn --ios --simulator --unoptimized" + exit 1 +fi + +autoninja -C out/$FLUTTER_ENGINE ios_test_flutter popd -./run_tests.sh ios_debug_sim_unopt +./run_tests.sh $FLUTTER_ENGINE From 4a1085a6f22a1953b6bdd7e33bd16f007745ba4d Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 13 Apr 2020 13:30:52 -0700 Subject: [PATCH 376/386] [web] Fix compositing order when adding paragraph tags (#17645) * Add test and dartfmt * Update golden locks * Addressed review comments * Fix lint error (unused widthConstraint) * Add maxDiffRatePercent for text diff on mac/linux --- lib/web_ui/dev/goldens_lock.yaml | 2 +- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 30 ++++++++--- lib/web_ui/lib/src/engine/canvas_pool.dart | 9 ++-- .../engine/canvas_draw_image_golden_test.dart | 54 +++++++++++++++++-- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index 137070e356b29..084f6e21c666d 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: 5ae87c98ad4abf882a2d312e4c4f75d7ad6d845b +revision: 1839716cce5da0fd4c442395c7161f29dd983d8e diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index 5444086f8c24c..e0ba89020de02 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -355,19 +355,18 @@ class BitmapCanvas extends EngineCanvas { void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { _drawImage(image, p, paint); _childOverdraw = true; - _canvasPool.allocateExtraCanvas(); + _canvasPool.closeCurrentCanvas(); } - html.ImageElement _drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { + html.ImageElement _drawImage( + ui.Image image, ui.Offset p, SurfacePaintData paint) { final HtmlImage htmlImage = image; final html.Element imgElement = htmlImage.cloneImageElement(); final ui.BlendMode blendMode = paint.blendMode; imgElement.style.mixBlendMode = _stringForBlendMode(blendMode); if (_canvasPool.isClipped) { // Reset width/height since they may have been previously set. - imgElement.style - ..removeProperty('width') - ..removeProperty('height'); + imgElement.style..removeProperty('width')..removeProperty('height'); final List clipElements = _clipContent( _canvasPool._clipStack, imgElement, p, _canvasPool.currentTransform); for (html.Element clipElement in clipElements) { @@ -418,7 +417,8 @@ class BitmapCanvas extends EngineCanvas { } } - final html.ImageElement imgElement = _drawImage(image, ui.Offset(targetLeft, targetTop), paint); + final html.ImageElement imgElement = + _drawImage(image, ui.Offset(targetLeft, targetTop), paint); // To scale set width / height on destination image. // For clipping we need to scale according to // clipped-width/full image width and shift it according to left/top of @@ -436,8 +436,22 @@ class BitmapCanvas extends EngineCanvas { if (requiresClipping) { restore(); } - _canvasPool.allocateExtraCanvas(); } + _closeCurrentCanvas(); + } + + // Should be called when we add new html elements into rootElement so that + // paint order is preserved. + // + // For example if we draw a path and then a paragraph and image: + // - rootElement + // |--- + // |---

+ // |--- + // Any drawing operations after these tags should allocate a new canvas, + // instead of drawing into earlier canvas. + void _closeCurrentCanvas() { + _canvasPool.closeCurrentCanvas(); _childOverdraw = true; } @@ -505,7 +519,6 @@ class BitmapCanvas extends EngineCanvas { final html.Element paragraphElement = _drawParagraphElement(paragraph, offset); - if (_canvasPool.isClipped) { final List clipElements = _clipContent( _canvasPool._clipStack, @@ -524,6 +537,7 @@ class BitmapCanvas extends EngineCanvas { rootElement.append(paragraphElement); } _children.add(paragraphElement); + _closeCurrentCanvas(); } /// Paints the [picture] into this canvas. diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index b97ffede6f0ae..56d8020023359 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -9,7 +9,7 @@ part of engine; /// /// [BitmapCanvas] signals allocation of first canvas using allocateCanvas. /// When a painting command such as drawImage or drawParagraph requires -/// multiple canvases for correct compositing, it calls [allocateExtraCanvas] +/// multiple canvases for correct compositing, it calls [closeCurrentCanvas] /// and adds the canvas(s) to a [_pool] of active canvas(s). /// /// To make sure transformations and clips are preserved correctly when a new @@ -54,9 +54,12 @@ class _CanvasPool extends _SaveStackTracking { return _contextHandle; } - // Allocating extra canvas items. Save current canvas so we can dispose + // Prevents active canvas to be used for rendering and prepares a new + // canvas allocation on next drawing request that will require one. + // + // Saves current canvas so we can dispose // and replay the clip/transform stack on top of new canvas. - void allocateExtraCanvas() { + void closeCurrentCanvas() { assert(_rootElement != null); // Place clean copy of current canvas with context stack restored and paint // reset into pool. diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart index 762c7b810fc5a..dcaf6c93b59bd 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart @@ -7,7 +7,7 @@ import 'dart:html' as html; import 'dart:math' as math; import 'dart:js_util' as js_util; -import 'package:ui/ui.dart' hide TextStyle; +import 'package:ui/ui.dart'; import 'package:ui/src/engine.dart'; import 'package:test/test.dart'; @@ -22,7 +22,8 @@ void main() async { // Commit a recording canvas to a bitmap, and compare with the expected Future _checkScreenshot(RecordingCanvas rc, String fileName, - {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { + {Rect region = const Rect.fromLTWH(0, 0, 500, 500), + double maxDiffRatePercent = 0.0}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); rc.apply(engineCanvas); @@ -32,7 +33,8 @@ void main() async { try { sceneElement.append(engineCanvas.rootElement); html.document.body.append(sceneElement); - await matchGoldenFile('$fileName.png', region: region, maxDiffRatePercent: 0.0); + await matchGoldenFile('$fileName.png', + region: region, maxDiffRatePercent: maxDiffRatePercent); } finally { // The page is reused across tests, so remove the element after taking the // Scuba screenshot. @@ -310,6 +312,39 @@ void main() async { rc.restore(); await _checkScreenshot(rc, 'draw_circle_on_image_clip_path'); }); + + // Regression test for https://github.com/flutter/flutter/issues/53078 + // Verified that Text+Image+Text+Rect+Text composites correctly. + // Yellow text should be behind image and rectangle. + // Cyan text should be above everything. + test('Paints text above and below image', () async { + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + rc.save(); + Image testImage = createTestImage(); + double testWidth = testImage.width.toDouble(); + double testHeight = testImage.height.toDouble(); + final Paragraph paragraph1 = createTestParagraph( + 'should be below...............', + color: Color(0xFFFFFF40)); + paragraph1.layout(const ParagraphConstraints(width: 400.0)); + rc.drawParagraph(paragraph1, const Offset(20, 100)); + rc.drawImageRect(testImage, Rect.fromLTRB(0, 0, testWidth, testHeight), + Rect.fromLTRB(100, 100, 200, 200), Paint()); + rc.drawRect( + Rect.fromLTWH(50, 50, 100, 200), + Paint() + ..strokeWidth = 3 + ..color = Color(0xA0000000)); + final Paragraph paragraph2 = createTestParagraph( + 'Should be above...............', + color: Color(0xFF00FFFF)); + paragraph2.layout(const ParagraphConstraints(width: 400.0)); + rc.drawParagraph(paragraph2, const Offset(20, 150)); + rc.restore(); + await _checkScreenshot(rc, 'draw_text_composite_order_below', + maxDiffRatePercent: 1.0); + }); } HtmlImage createTestImage({int width = 100, int height = 50}) { @@ -329,3 +364,16 @@ HtmlImage createTestImage({int width = 100, int height = 50}) { imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); return HtmlImage(imageElement, width, height); } + +Paragraph createTestParagraph(String text, + {Color color = const Color(0xFF000000)}) { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 14.0, + )); + builder.pushStyle(TextStyle(color: color)); + builder.addText(text); + return builder.build(); +} From fd1db54e02e7bf1f7e4699b3bd8d316f0f2ffca6 Mon Sep 17 00:00:00 2001 From: George Wright Date: Mon, 13 Apr 2020 16:45:03 -0700 Subject: [PATCH 377/386] Use VK_LAYER_KHRONOS_validation only on Fuchsia (#17683) --- tools/fuchsia/fuchsia_libs.gni | 47 +++------------------------------- vulkan/vulkan_utilities.cc | 11 +++----- 2 files changed, 7 insertions(+), 51 deletions(-) diff --git a/tools/fuchsia/fuchsia_libs.gni b/tools/fuchsia/fuchsia_libs.gni index 47336f8e48255..75443f96a6575 100644 --- a/tools/fuchsia/fuchsia_libs.gni +++ b/tools/fuchsia/fuchsia_libs.gni @@ -72,63 +72,22 @@ common_libs = [ vulkan_dist = "$fuchsia_sdk_base/dist" +# Note that the other validation libraries in the Fuchsia SDK seem to have a bug right +# now causing crashes, so it is only recommended that we use +# VkLayer_khronos_validation.so until we have a confirmation that they are fixed. vulkan_validation_libs = [ - { - name = "VkLayer_core_validation.so" - path = rebase_path("$vulkan_dist") - }, { name = "VkLayer_khronos_validation.so" path = rebase_path("$vulkan_dist") }, - { - name = "VkLayer_object_lifetimes.so" - path = rebase_path("$vulkan_dist") - }, - { - name = "VkLayer_stateless_validation.so" - path = rebase_path("$vulkan_dist") - }, - { - name = "VkLayer_thread_safety.so" - path = rebase_path("$vulkan_dist") - }, - { - name = "VkLayer_unique_objects.so" - path = rebase_path("$vulkan_dist") - }, ] vulkan_data_dir = "//fuchsia/sdk/linux/pkg/vulkan_layers/data/vulkan/explicit_layer.d" vulkan_icds = [ - { - path = rebase_path("$vulkan_data_dir/VkLayer_core_validation.json") - dest = "vulkan/explicit_layer.d/VkLayer_core_validation.json" - }, - { - path = rebase_path("$vulkan_data_dir/VkLayer_object_lifetimes.json") - dest = "vulkan/explicit_layer.d/VkLayer_object_lifetimes.json" - }, - { - path = rebase_path("$vulkan_data_dir/VkLayer_thread_safety.json") - dest = "vulkan/explicit_layer.d/VkLayer_thread_safety.json" - }, - { - path = rebase_path("$vulkan_data_dir/VkLayer_stateless_validation.json") - dest = "vulkan/explicit_layer.d/VkLayer_stateless_validation.json" - }, - { - path = rebase_path("$vulkan_data_dir/VkLayer_unique_objects.json") - dest = "vulkan/explicit_layer.d/VkLayer_unique_objects.json" - }, { path = rebase_path("$vulkan_data_dir/VkLayer_khronos_validation.json") dest = "vulkan/explicit_layer.d/VkLayer_khronos_validation.json" }, - { - path = rebase_path("$vulkan_data_dir/VkLayer_standard_validation.json") - dest = "vulkan/explicit_layer.d/VkLayer_standard_validation.json" - }, ] diff --git a/vulkan/vulkan_utilities.cc b/vulkan/vulkan_utilities.cc index d02602104e328..61de451afa283 100644 --- a/vulkan/vulkan_utilities.cc +++ b/vulkan/vulkan_utilities.cc @@ -43,13 +43,10 @@ static std::vector InstanceOrDeviceLayersToEnable( // NOTE: The loader is sensitive to the ordering here. Please do not rearrange // this list. #if OS_FUCHSIA - // Fuchsia uses the updated Vulkan loader and validation layers which no - // longer includes the image validation layer. - const std::vector candidates = { - "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", - "VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation", - "VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_swapchain", - "VK_LAYER_GOOGLE_unique_objects"}; + // The other layers in the Fuchsia SDK seem to have a bug right now causing + // crashes, so it is only recommended that we use VK_LAYER_KHRONOS_validation + // until we have a confirmation that they are fixed. + const std::vector candidates = {"VK_LAYER_KHRONOS_validation"}; #else const std::vector candidates = { "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", From d8632496d46e7785461ab4185b0704a080ebed95 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 19:50:08 -0400 Subject: [PATCH 378/386] Roll src/third_party/skia d468a1619a2f..f3953d04a0b8 (11 commits) (#17685) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 5d4df0027fe06..d3762ba7e7744 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'd468a1619a2f3fe7ba0df668e949c0492c2a87f6', + 'skia_revision': 'f3953d04a0b84e8e8f621d69cb6a630bd448f2f2', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f3eb922d871ed..0aff82124c79c 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: d5e054dd8461b290526b5ec7d32fe7e7 +Signature: c84bffa2069989f133dc96953d12ba27 UNUSED LICENSES: @@ -3902,6 +3902,8 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DRenderTarget.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceState.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DStencilAttachment.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DStencilAttachment.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTextureRenderTarget.cpp From 053da38449a6f013462becd0a5e88d672af17e9d Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Mon, 13 Apr 2020 16:50:11 -0700 Subject: [PATCH 379/386] In tests run dart code on ui(rather than on platform) thread. (#17686) --- testing/dart_isolate_runner.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/dart_isolate_runner.cc b/testing/dart_isolate_runner.cc index 7212879d18407..8fe1d1f5ee09b 100644 --- a/testing/dart_isolate_runner.cc +++ b/testing/dart_isolate_runner.cc @@ -55,7 +55,7 @@ void RunDartCodeInIsolate(DartVMRef& vm_ref, const std::vector& args, const std::string& fixtures_path, fml::WeakPtr io_manager) { - FML_CHECK(task_runners.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + FML_CHECK(task_runners.GetUITaskRunner()->RunsTasksOnCurrentThread()); if (!vm_ref) { return; @@ -163,7 +163,7 @@ std::unique_ptr RunDartCodeInIsolate( std::unique_ptr result; fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners.GetPlatformTaskRunner(), fml::MakeCopyable([&]() mutable { + task_runners.GetUITaskRunner(), fml::MakeCopyable([&]() mutable { RunDartCodeInIsolate(vm_ref, result, settings, task_runners, entrypoint, args, fixtures_path, io_manager); latch.Signal(); From 43e0a984106fe6bb0665920bb5d232ad68a4bc69 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 20:50:01 -0400 Subject: [PATCH 380/386] Roll fuchsia/sdk/core/mac-amd64 from G7eYf... to 8JtFK... (#17689) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d3762ba7e7744..846a97b519c8b 100644 --- a/DEPS +++ b/DEPS @@ -538,7 +538,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'G7eYf_FUdJN8_BqA8wdx1P_YnsRDndhTJ4hvAVd5uT8C' + 'version': '8JtFK64mmIC2zTEj9ICMrcQBITqKDZVQluLVKczro9kC' } ], 'condition': 'host_os == "mac"', From f82a615ffb6902e45e059d1d3fb546850f577194 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 13 Apr 2020 23:35:02 -0400 Subject: [PATCH 381/386] Roll src/third_party/skia f3953d04a0b8..4f17b60208fa (9 commits) (#17693) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 846a97b519c8b..22b9cc5b6f2bd 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f3953d04a0b84e8e8f621d69cb6a630bd448f2f2', + 'skia_revision': '4f17b60208fa022309ecce72b99bacf942f5ca09', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 0aff82124c79c..34c900407f027 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c84bffa2069989f133dc96953d12ba27 +Signature: 52c8a4241a52cae12cef756c426227a7 UNUSED LICENSES: From b360eab6f7e21769a0939696c2643a34f79b5d93 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 14 Apr 2020 15:41:23 +1200 Subject: [PATCH 382/386] Move Linux shell docstrings to headers (#17410) Update docstrings for typos and grammar style specified in the style guide. --- shell/platform/linux/fl_dart_project.cc | 48 -------------- shell/platform/linux/fl_view.cc | 14 ----- .../public/flutter_linux/fl_dart_project.h | 63 +++++++++++++++++++ .../linux/public/flutter_linux/fl_view.h | 14 +++++ 4 files changed, 77 insertions(+), 62 deletions(-) diff --git a/shell/platform/linux/fl_dart_project.cc b/shell/platform/linux/fl_dart_project.cc index 0a20dd6ade897..fcaeff2248618 100644 --- a/shell/platform/linux/fl_dart_project.cc +++ b/shell/platform/linux/fl_dart_project.cc @@ -6,13 +6,6 @@ #include -/** - * FlDartProject: - * - * #FlDartProject represents a Dart project. It is used provide information - * about the application when creating a #FlView. - */ - struct _FlDartProject { GObject parent_instance; @@ -112,62 +105,21 @@ static void fl_dart_project_class_init(FlDartProjectClass* klass) { static void fl_dart_project_init(FlDartProject* self) {} -/** - * fl_dart_project_new: - * @path: a file path, e.g. "my_dart_project" - * - * Create a Flutter project. The project path should contain the following - * top-level items: - * - icudtl.dat (provided as a resource by the Flutter tool) - * - flutter_assets (as built by the Flutter tool) - * - * The path can either be absolute, or relative to the directory containing the - * running executable. - * - * Returns: a new #FlDartProject - */ G_MODULE_EXPORT FlDartProject* fl_dart_project_new(const gchar* path) { return static_cast( g_object_new(fl_dart_project_get_type(), "path", path, nullptr)); } -/** - * fl_dart_project_get_path: - * @view: a #FlDartProject - * - * Get the path to the directory containing the Flutter application. - * - * Returns: (type filename): a file path, e.g. "/projects/my_dart_project" - */ G_MODULE_EXPORT const gchar* fl_dart_project_get_path(FlDartProject* self) { g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); return self->path; } -/** - * fl_dart_project_get_assets_path: - * @view: a #FlDartProject - * - * Get the path to the directory containing the assets used in the Flutter - * application. - * - * Returns: (type filename): a file path, e.g. - * "/projects/my_dart_project/assets" - */ G_MODULE_EXPORT gchar* fl_dart_project_get_assets_path(FlDartProject* self) { g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); return g_build_filename(self->path, "flutter_assets", NULL); } -/** - * fl_dart_project_get_icu_data_path: - * @view: a #FlDartProject - * - * Get the path to the ICU data file in the Flutter application. - * - * Returns: (type filename): a file path, e.g. - * "/projects/my_dart_project/icudtl.dat" - */ G_MODULE_EXPORT gchar* fl_dart_project_get_icu_data_path(FlDartProject* self) { g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); return g_build_filename(self->path, "icudtl.dat", NULL); diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 879a36201ac17..a1aad509a5230 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -11,12 +11,6 @@ #include "flutter/shell/platform/embedder/embedder.h" -/** - * FlView: - * - * #FlView is a GTK widget that is capable of displaying a Flutter application. - */ - struct _FlView { GtkWidget parent_instance; @@ -278,14 +272,6 @@ static void fl_view_class_init(FlViewClass* klass) { static void fl_view_init(FlView* self) {} -/** - * fl_view_new: - * @project: The project to show. - * - * Create a widget to show Flutter application. - * - * Returns: a new #FlView - */ G_MODULE_EXPORT FlView* fl_view_new(FlDartProject* project) { return static_cast( g_object_new(fl_view_get_type(), "flutter-project", project, nullptr)); diff --git a/shell/platform/linux/public/flutter_linux/fl_dart_project.h b/shell/platform/linux/public/flutter_linux/fl_dart_project.h index 8ee42502179eb..1a379713ccc06 100644 --- a/shell/platform/linux/public/flutter_linux/fl_dart_project.h +++ b/shell/platform/linux/public/flutter_linux/fl_dart_project.h @@ -15,12 +15,75 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(FlDartProject, fl_dart_project, FL, DART_PROJECT, GObject) +/** + * FlDartProject: + * + * #FlDartProject represents a Dart project. It is used to provide information + * about the application when creating a #FlView. + */ + +/** + * fl_dart_project_new: + * @path: a file path, e.g. "my_dart_project" + * + * Create a Flutter project. The project path should contain the following + * top-level items: + * - icudtl.dat (provided as a resource by the Flutter tool) + * - flutter_assets (as built by the Flutter tool) + * + * The path can either be absolute, or relative to the directory containing the + * running executable. + * + * Returns: a new #FlDartProject + */ + +/** + * fl_dart_project_new: + * @path: a file path, e.g. "my_dart_project" + * + * Creates a Flutter project. The project path should contain the following + * top-level items: + * - icudtl.dat (provided as a resource by the Flutter tool) + * - flutter_assets (as built by the Flutter tool) + * + * The path can either be absolute, or relative to the directory containing the + * running executable. + * + * Returns: a new #FlDartProject + */ FlDartProject* fl_dart_project_new(const gchar* path); +/** + * fl_dart_project_get_path: + * @project: a #FlDartProject + * + * Gets the path to the directory containing the Flutter application. + * + * Returns: (type filename): a file path, e.g. "/projects/my_dart_project" + */ const gchar* fl_dart_project_get_path(FlDartProject* project); +/** + * fl_dart_project_get_assets_path: + * @project: a #FlDartProject + * + * Gets the path to the directory containing the assets used in the Flutter + * application. + * + * Returns: (type filename): a file path, e.g. + * "/projects/my_dart_project/assets" + */ gchar* fl_dart_project_get_assets_path(FlDartProject* project); +/** + * fl_dart_project_get_icu_data_path: + * @project: a #FlDartProject + * + * Gets the path to the ICU data file in the Flutter application. + * + * Returns: (type filename): a file path, e.g. + * "/projects/my_dart_project/icudtl.dat" + */ gchar* fl_dart_project_get_icu_data_path(FlDartProject* project); G_END_DECLS diff --git a/shell/platform/linux/public/flutter_linux/fl_view.h b/shell/platform/linux/public/flutter_linux/fl_view.h index a47733e251747..6b93daebfb352 100644 --- a/shell/platform/linux/public/flutter_linux/fl_view.h +++ b/shell/platform/linux/public/flutter_linux/fl_view.h @@ -17,6 +17,20 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(FlView, fl_view, FL, VIEW, GtkWidget) +/** + * FlView: + * + * #FlView is a GTK widget that is capable of displaying a Flutter application. + */ + +/** + * fl_view_new: + * @project: The project to show. + * + * Creates a widget to show Flutter application. + * + * Returns: a new #FlView + */ FlView* fl_view_new(FlDartProject* project); G_END_DECLS From 6bbdf33b0ceef2459cef4f5f2493307ee002cb5d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 14 Apr 2020 00:20:01 -0400 Subject: [PATCH 383/386] Roll src/third_party/dart 2aecf30d2b2e..4aa896464a48 (26 commits) (#17694) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 22b9cc5b6f2bd..ce8ace200de9f 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '2aecf30d2b2e8e5bad0da003d0411c9a8489363a', + 'dart_revision': '4aa896464a4860f2e8e35c9f31b4742aa3ed3dc1', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 67b0ccbb6a6b6..18cdd40a7c8e2 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: ca93d840288cbd1c9634445936ba08e1 +Signature: cdaa56a5f77c8a504af3e424af40fb9f UNUSED LICENSES: From d3b2da0385ee3d38ba18fc540272378dc40447da Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 14 Apr 2020 02:45:02 -0400 Subject: [PATCH 384/386] Roll src/third_party/skia 4f17b60208fa..f50063625a0c (1 commits) (#17696) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ce8ace200de9f..bea8618f63ddd 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4f17b60208fa022309ecce72b99bacf942f5ca09', + 'skia_revision': 'f50063625a0c04424f73129d3d20b11e6889510b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 34c900407f027..1c995652dac2b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 52c8a4241a52cae12cef756c426227a7 +Signature: 214dab68e1274ac84cafadb95654adbb UNUSED LICENSES: From 662d7977a50deb638baa15af06d32cd8055d3279 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 14 Apr 2020 05:40:02 -0400 Subject: [PATCH 385/386] Roll src/third_party/skia f50063625a0c..ad653d8378d7 (3 commits) (#17697) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index bea8618f63ddd..c0ef07f84ce78 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f50063625a0c04424f73129d3d20b11e6889510b', + 'skia_revision': 'ad653d8378d7a17502956c4addebb68eb3129961', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 1c995652dac2b..8f0b0639ec3f7 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 214dab68e1274ac84cafadb95654adbb +Signature: 2da5a3733cfc3ee9d4098855a1f98d15 UNUSED LICENSES: From 7dccc731b425be66de2682c9682efc1849653919 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 14 Apr 2020 06:50:01 -0400 Subject: [PATCH 386/386] Roll src/third_party/dart 4aa896464a48..3e43a3dcadf9 (3 commits) (#17698) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index c0ef07f84ce78..1af4d736d7562 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '4aa896464a4860f2e8e35c9f31b4742aa3ed3dc1', + 'dart_revision': '3e43a3dcadf96c0f1e30b12e0a1805df5a336c3c', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py

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 Kxl;Y>5C09?DAs8J From f1d8026637e276b4b2f2d85f74b15f469e135b6b Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Tue, 24 Mar 2020 22:00:21 -0700 Subject: [PATCH 127/386] [metrics] Record the frame target time on the layer tree (#17281) This lets us measure stats on when the frame was scheduled to be rendered vs when it finished rasterizing. Note: This isn't propagated to the FrameTimings struct yet, that is to be followed. --- flow/layers/layer_tree.cc | 6 ++++-- flow/layers/layer_tree.h | 4 +++- shell/common/animator.cc | 9 ++++++--- shell/common/animator.h | 3 ++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index 61560abbed5d4..e7fc0ceadcc2f 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc @@ -21,8 +21,10 @@ LayerTree::LayerTree(const SkISize& frame_size, checkerboard_raster_cache_images_(false), checkerboard_offscreen_layers_(false) {} -void LayerTree::RecordBuildTime(fml::TimePoint start) { - build_start_ = start; +void LayerTree::RecordBuildTime(fml::TimePoint build_start, + fml::TimePoint target_time) { + build_start_ = build_start; + target_time_ = target_time; build_finish_ = fml::TimePoint::Now(); } diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index 43fc58a9746b6..39a88a30ae05c 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h @@ -54,10 +54,11 @@ class LayerTree { float frame_physical_depth() const { return frame_physical_depth_; } float frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; } - void RecordBuildTime(fml::TimePoint begin_start); + void RecordBuildTime(fml::TimePoint build_start, fml::TimePoint target_time); fml::TimePoint build_start() const { return build_start_; } fml::TimePoint build_finish() const { return build_finish_; } fml::TimeDelta build_time() const { return build_finish_ - build_start_; } + fml::TimePoint target_time() const { return target_time_; } // The number of frame intervals missed after which the compositor must // trace the rasterized picture to a trace file. Specify 0 to disable all @@ -84,6 +85,7 @@ class LayerTree { std::shared_ptr root_layer_; fml::TimePoint build_start_; fml::TimePoint build_finish_; + fml::TimePoint target_time_; SkISize frame_size_ = SkISize::MakeEmpty(); // Physical pixels. float frame_physical_depth_; float frame_device_pixel_ratio_ = 1.0f; // Logical / Physical pixels ratio. diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 5ae3862a93ad1..626e557c61b02 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -25,7 +25,8 @@ Animator::Animator(Delegate& delegate, : delegate_(delegate), task_runners_(std::move(task_runners)), waiter_(std::move(waiter)), - last_begin_frame_time_(), + last_frame_begin_time_(), + last_frame_target_time_(), dart_frame_deadline_(0), #if FLUTTER_SHELL_ENABLE_METAL layer_tree_pipeline_(fml::MakeRefCounted(2)), @@ -132,7 +133,8 @@ void Animator::BeginFrame(fml::TimePoint frame_start_time, // to service potential frame. FML_DCHECK(producer_continuation_); - last_begin_frame_time_ = frame_start_time; + last_frame_begin_time_ = frame_start_time; + last_frame_target_time_ = frame_target_time; dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time); { TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame", @@ -178,7 +180,8 @@ void Animator::Render(std::unique_ptr layer_tree) { if (layer_tree) { // Note the frame time for instrumentation. - layer_tree->RecordBuildTime(last_begin_frame_time_); + layer_tree->RecordBuildTime(last_frame_begin_time_, + last_frame_target_time_); } // Commit the pending continuation. diff --git a/shell/common/animator.h b/shell/common/animator.h index 0bb15bed72a54..ba5e1f4df40a3 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -96,7 +96,8 @@ class Animator final { TaskRunners task_runners_; std::shared_ptr waiter_; - fml::TimePoint last_begin_frame_time_; + fml::TimePoint last_frame_begin_time_; + fml::TimePoint last_frame_target_time_; int64_t dart_frame_deadline_; fml::RefPtr layer_tree_pipeline_; fml::Semaphore pending_frame_semaphore_; From e1d6f373f1e4c3b2d919e4e011de72ae10591c15 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 02:56:03 -0400 Subject: [PATCH 128/386] Roll src/third_party/skia 6beb1dd941af..4261fe753b77 (3 commits) (#17309) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 74a0145e289fe..88a5dd0a62891 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6beb1dd941afe8ded876a25bee21b529d95ead30', + 'skia_revision': '4261fe753b772bb078714260e6951108b99434f6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 794bda9a00f45..f09b8347c8a18 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9590eced250fca00e288000c0c9e18e8 +Signature: 2f39a880d70f48142e4b7221a5b33ce5 UNUSED LICENSES: From 5d0c59f01ae14fd3ef1c5cf659571fad51ff83d0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 05:11:02 -0400 Subject: [PATCH 129/386] Roll src/third_party/skia 4261fe753b77..9686528ac8c7 (1 commits) (#17314) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 88a5dd0a62891..d4dbc6479de25 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4261fe753b772bb078714260e6951108b99434f6', + 'skia_revision': '9686528ac8c752563425f73237aa0ed60dcbc0dd', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f09b8347c8a18..8956726d8f99d 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 2f39a880d70f48142e4b7221a5b33ce5 +Signature: 643acc34c29fcde7f78a3de26325c033 UNUSED LICENSES: From ea1268308dbbdbbb7cf3819e87cd73313fe3c308 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 07:21:02 -0400 Subject: [PATCH 130/386] Roll fuchsia/sdk/core/mac-amd64 from Lzj3Z... to o8vhc... (#17315) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d4dbc6479de25..73d8507276de6 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'Lzj3ZGYb0b41hEejAnPUyKdem5OZ6ducD5MkHB70eFwC' + 'version': 'o8vhcBMhLFl1PCgFR9WoyT4-Xv-wocqDzZt97bain4UC' } ], 'condition': 'host_os == "mac"', From 90cc66ce05c26ab577047bebfd67ac557ddfe2ee Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 07:31:02 -0400 Subject: [PATCH 131/386] Roll fuchsia/sdk/core/linux-amd64 from tFo5u... to jHDPP... (#17316) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 73d8507276de6..98d382dc3f380 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'tFo5u0C7qYIH67r0WUzb4QqxBu8F9YqJSEBCAicTiuYC' + 'version': 'jHDPP4XNSmH-VqBjuMtIMYWvpboXqbqAf4oP4FT-KC0C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 8ac52cbe98034..6a8f36f115d5a 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: d04347b220b4d7cc3e65c644cc7a76f1 +Signature: 617bc193a64c9a737e35dd89a3c62975 UNUSED LICENSES: From 1eb2e78417d7de5fbcb867797757f9cba2399f60 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 10:06:02 -0400 Subject: [PATCH 132/386] Roll src/third_party/skia 9686528ac8c7..ae58199380dd (1 commits) (#17317) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 98d382dc3f380..8531416076695 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '9686528ac8c752563425f73237aa0ed60dcbc0dd', + 'skia_revision': 'ae58199380dd01fb0041d01f87e698be730762b7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 8956726d8f99d..b934ee34df8f6 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 643acc34c29fcde7f78a3de26325c033 +Signature: 9da87e39816107c59477546493f5c9f4 UNUSED LICENSES: From edfe06c11304538739e5abf715962fa79b7c8e00 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Wed, 25 Mar 2020 09:08:15 -0700 Subject: [PATCH 133/386] Add the `chromedriver/` directory to gitignore This folder is generated by the integration test manager. --- lib/web_ui/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web_ui/.gitignore b/lib/web_ui/.gitignore index 567609b1234a9..afc0a9b3df77e 100644 --- a/lib/web_ui/.gitignore +++ b/lib/web_ui/.gitignore @@ -1 +1,2 @@ build/ +chromedriver/ From dd102bf228af2199c5a6b8e099b15cbbdc0621fa Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 12:11:03 -0400 Subject: [PATCH 134/386] Roll src/third_party/skia ae58199380dd..102411c73f7e (6 commits) (#17318) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 8531416076695..7b66a751d8352 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'ae58199380dd01fb0041d01f87e698be730762b7', + 'skia_revision': '102411c73f7e3ddc388c2b898cc2791fca89be62', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index b934ee34df8f6..03f3e34612762 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9da87e39816107c59477546493f5c9f4 +Signature: 5fb5973c68a6abb6d205d6498407ddfa UNUSED LICENSES: From a7742c54f72ead1aef02b34ae660bb4d2ea82b8c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 14:26:04 -0400 Subject: [PATCH 135/386] Roll src/third_party/skia 102411c73f7e..f11e33152460 (3 commits) (#17319) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7b66a751d8352..029e81d7b2614 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '102411c73f7e3ddc388c2b898cc2791fca89be62', + 'skia_revision': 'f11e331524605268df714fa41f9a9046c3f08c3e', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 03f3e34612762..a40285cdb76c3 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 5fb5973c68a6abb6d205d6498407ddfa +Signature: b39e6f199638d78c07e950bfaf5d74a7 UNUSED LICENSES: From e79aef61d166a85ae74dea3e304d8a59a9813e24 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 14:31:03 -0400 Subject: [PATCH 136/386] Roll src/third_party/dart 62f84880ef15..232308723704 (54 commits) (#17320) --- DEPS | 6 +++--- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 029e81d7b2614..932004192c90d 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '62f84880ef15de299d826a39a20b48b8dd497816', + 'dart_revision': '232308723704677c2a3daa6e2f4bd77a5a7b6510', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -71,13 +71,13 @@ vars = { 'dart_mustache_tag': '5e81b12215566dbe2473b2afd01a8a8aedd56ad9', 'dart_oauth2_tag': '1.2.1', 'dart_observatory_pub_packages_rev': '0894122173b0f98eb08863a7712e78407d4477bc', - 'dart_package_config_tag': '87a8b5184020ebcc13b34ee95dde58f851b68ca3', + 'dart_package_config_tag': 'v1.9.2', 'dart_package_resolver_tag': '1.0.10', 'dart_path_tag': '1.6.2', 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '11afdac99fe67373829f37acc75b625011d360ad', + 'dart_pub_rev': 'ab109723d9eb782c2ea4cce68c044da40403a652', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 661f33f2de217..95d8981684ce6 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 6f5106681d237e2b200b77f91e114c1e +Signature: c15991dd303125cadfea57eb6d3c3bcf UNUSED LICENSES: From 04182cde5db00114e55300ae0196a87b24ba1d7e Mon Sep 17 00:00:00 2001 From: Ferhat Date: Wed, 25 Mar 2020 12:41:17 -0700 Subject: [PATCH 137/386] Switch ui.window.devicePixelRatio from browser logical to physical. (#17209) * Switch ui.window.devicePixelRatio to browser dpi * Add integration test for multi res image loading * Update cirrus for new integration test * update canvas golden test root transform * Update compositing golden test with dpr transform --- .cirrus.yml | 1 + .../assets/images/1.5x/sample_image1.png | Bin 0 -> 28340 bytes .../assets/images/2.0x/sample_image1.png | Bin 0 -> 41558 bytes .../assets/images/sample_image1.png | Bin 0 -> 14746 bytes .../lib/image_loading_main.dart | 32 ++++++++++ .../regular_integration_tests/pubspec.yaml | 4 ++ .../test_driver/image_loading_e2e.dart | 25 ++++++++ .../test_driver/image_loading_e2e_test.dart | 17 +++++ .../src/engine/compositor/embedded_views.dart | 2 + lib/web_ui/lib/src/engine/dom_renderer.dart | 6 -- lib/web_ui/lib/src/engine/platform_views.dart | 2 + .../src/engine/semantics/accessibility.dart | 4 +- .../lib/src/engine/surface/scene_builder.dart | 31 ++++++--- .../src/engine/text_editing/text_editing.dart | 8 ++- lib/web_ui/lib/src/engine/window.dart | 59 ++++++++++++------ lib/web_ui/lib/src/ui/test_embedding.dart | 2 +- .../engine/semantics/accessibility_test.dart | 3 +- .../engine/canvas_golden_test.dart | 3 + .../engine/compositing_golden_test.dart | 6 ++ lib/web_ui/test/text_editing_test.dart | 2 +- 20 files changed, 168 insertions(+), 39 deletions(-) create mode 100644 e2etests/web/regular_integration_tests/assets/images/1.5x/sample_image1.png create mode 100644 e2etests/web/regular_integration_tests/assets/images/2.0x/sample_image1.png create mode 100644 e2etests/web/regular_integration_tests/assets/images/sample_image1.png create mode 100644 e2etests/web/regular_integration_tests/lib/image_loading_main.dart create mode 100644 e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart create mode 100644 e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index 441e3ceec3937..77d8b81ca4a6f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -146,6 +146,7 @@ task: - $FRAMEWORK_PATH/flutter/bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web - $FRAMEWORK_PATH/flutter/bin/flutter pub get --local-engine=host_debug_unopt - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/text_editing_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt + - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/image_loading_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt - name: build_and_test_web_linux_firefox compile_host_script: | diff --git a/e2etests/web/regular_integration_tests/assets/images/1.5x/sample_image1.png b/e2etests/web/regular_integration_tests/assets/images/1.5x/sample_image1.png new file mode 100644 index 0000000000000000000000000000000000000000..f1e0ffab2000794b4383822e004bf7438d25ad73 GIT binary patch literal 28340 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 e(}mphinJVDosk2bwfW}( + 'setDevicePixelRatio', + '1.5', + ); + runApp(MyApp()); +} + +class MyApp extends StatefulWidget { + @override + MyAppState createState() => MyAppState(); +} + +class MyAppState extends State { + @override + Widget build(BuildContext context) { + return MaterialApp( + key: const Key('mainapp'), + title: 'Integration Test App', + home: Image.asset('assets/images/sample_image1.png') + ); + } +} diff --git a/e2etests/web/regular_integration_tests/pubspec.yaml b/e2etests/web/regular_integration_tests/pubspec.yaml index 98fa40773834e..7f70f838183db 100644 --- a/e2etests/web/regular_integration_tests/pubspec.yaml +++ b/e2etests/web/regular_integration_tests/pubspec.yaml @@ -16,3 +16,7 @@ dev_dependencies: e2e: 0.2.4+4 http: 0.12.0+2 test: any + +flutter: + assets: + - assets/images/ \ No newline at end of file diff --git a/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart new file mode 100644 index 0000000000000..bc6ebb9dba887 --- /dev/null +++ b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:regular_integration_tests/image_loading_main.dart' as app; + +import 'package:e2e/e2e.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + testWidgets('Image loads asset variant based on device pixel ratio', + (WidgetTester tester) async { + app.main(); + await tester.pumpAndSettle(); + final html.ImageElement imageElement = html.querySelector('img') as html.ImageElement; + expect(imageElement.naturalWidth, 1.5 * 100); + expect(imageElement.naturalHeight, 1.5 * 100); + expect(imageElement.width, 100); + expect(imageElement.height, 100); + }); +} diff --git a/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart new file mode 100644 index 0000000000000..02edbe5679179 --- /dev/null +++ b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +Future main() async { + final FlutterDriver driver = await FlutterDriver.connect(); + + final String dataRequest = + await driver.requestData(null, timeout: const Duration(seconds: 1)); + await driver.close(); + + exit(dataRequest == 'pass' ? 0 : 1); +} diff --git a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart index 31f9dc176bafe..9deee4f7b60f5 100644 --- a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart @@ -69,9 +69,11 @@ class HtmlViewEmbedder { switch (decoded.method) { case 'create': _create(decoded, callback); + window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'dispose': _dispose(decoded, callback); + window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; } callback(null); diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index 8e2f585b83f20..a5d630dd59de7 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -419,12 +419,6 @@ flt-glass-pane * { // DOM tree. setElementAttribute(_sceneHostElement, 'aria-hidden', 'true'); - // We treat browser pixels as device pixels because pointer events, - // position, and sizes all use browser pixel as the unit (i.e. "px" in CSS). - // Therefore, as far as the framework is concerned the device pixel ratio - // is 1.0. - window.debugOverrideDevicePixelRatio(1.0); - if (html.window.visualViewport == null && isWebKit) { // Safari sometimes gives us bogus innerWidth/innerHeight values when the // page loads. When it changes the values to correct ones it does not diff --git a/lib/web_ui/lib/src/engine/platform_views.dart b/lib/web_ui/lib/src/engine/platform_views.dart index d422f9a3ecf84..2454cd44edb1d 100644 --- a/lib/web_ui/lib/src/engine/platform_views.dart +++ b/lib/web_ui/lib/src/engine/platform_views.dart @@ -50,9 +50,11 @@ void handlePlatformViewCall( switch (decoded.method) { case 'create': _createPlatformView(decoded, callback); + window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'dispose': _disposePlatformView(decoded, callback); + window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; } callback(null); diff --git a/lib/web_ui/lib/src/engine/semantics/accessibility.dart b/lib/web_ui/lib/src/engine/semantics/accessibility.dart index a858ebc7ab668..2d874f8cf01da 100644 --- a/lib/web_ui/lib/src/engine/semantics/accessibility.dart +++ b/lib/web_ui/lib/src/engine/semantics/accessibility.dart @@ -52,9 +52,9 @@ class AccessibilityAnnouncements { html.HtmlElement get _domElement => _element ??= _createElement(); /// Decodes the message coming from the 'flutter/accessibility' channel. - void handleMessage(ByteData data) { + void handleMessage(StandardMessageCodec codec, ByteData data) { final Map inputMap = - const StandardMessageCodec().decodeMessage(data); + codec.decodeMessage(data); final Map dataMap = inputMap['data']; final String message = dataMap['message']; if (message != null && message.isNotEmpty) { diff --git a/lib/web_ui/lib/src/engine/surface/scene_builder.dart b/lib/web_ui/lib/src/engine/surface/scene_builder.dart index 4de1fe779e55c..823a9963acddc 100644 --- a/lib/web_ui/lib/src/engine/surface/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/surface/scene_builder.dart @@ -10,7 +10,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { _surfaceStack.add(PersistedScene(_lastFrameScene)); } - final List _surfaceStack = []; + final List _surfaceStack = + []; /// The scene built by this scene builder. /// @@ -19,7 +20,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { assert(() { if (_surfaceStack.length != 1) { final String surfacePrintout = _surfaceStack - .map((PersistedContainerSurface surface) => surface.runtimeType) + .map( + (PersistedContainerSurface surface) => surface.runtimeType) .toList() .join(', '); throw Exception('Incorrect sequence of push/pop operations while ' @@ -42,7 +44,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { // the live tree. if (surface.oldLayer != null) { assert(surface.oldLayer.runtimeType == surface.runtimeType); - assert(debugAssertSurfaceState(surface.oldLayer, PersistedSurfaceState.active)); + assert(debugAssertSurfaceState( + surface.oldLayer, PersistedSurfaceState.active)); surface.oldLayer.state = PersistedSurfaceState.pendingUpdate; } _adoptSurface(surface); @@ -97,6 +100,16 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } + if (_surfaceStack.length == 1) { + // Top level transform contains view configuration to scale + // scene to devicepixelratio. Use identity instead since CSS uses + // logical device pixels. + if (!ui.debugEmulateFlutterTesterEnvironment) { + assert(matrix4[0] == window.devicePixelRatio && + matrix4[5] == window.devicePixelRatio); + } + matrix4 = Matrix4.identity().storage; + } return _pushSurface(PersistedTransform(oldLayer, matrix4)); } @@ -276,7 +289,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { void addRetained(ui.EngineLayer retainedLayer) { final PersistedContainerSurface retainedSurface = retainedLayer; if (assertionsEnabled) { - assert(debugAssertSurfaceState(retainedSurface, PersistedSurfaceState.active, PersistedSurfaceState.released)); + assert(debugAssertSurfaceState(retainedSurface, + PersistedSurfaceState.active, PersistedSurfaceState.released)); } retainedSurface.tryRetain(); _adoptSurface(retainedSurface); @@ -319,7 +333,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// for more details. @override void addPerformanceOverlay(int enabledOptions, ui.Rect bounds) { - _addPerformanceOverlay(enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); + _addPerformanceOverlay( + enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); } /// Whether we've already warned the user about the lack of the performance @@ -337,7 +352,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ) { if (!_webOnlyDidWarnAboutPerformanceOverlay) { _webOnlyDidWarnAboutPerformanceOverlay = true; - html.window.console.warn('The performance overlay isn\'t supported on the web'); + html.window.console + .warn('The performance overlay isn\'t supported on the web'); } } @@ -377,7 +393,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { _addTexture(offset.dx, offset.dy, width, height, textureId); } - void _addTexture(double dx, double dy, double width, double height, int textureId) { + void _addTexture( + double dx, double dy, double width, double height, int textureId) { // In test mode, allow this to be a no-op. if (!ui.debugEmulateFlutterTesterEnvironment) { throw UnimplementedError('Textures are not supported in Flutter Web'); diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index 369d711e59f38..b13b0a168e604 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -778,8 +778,11 @@ class TextEditingChannel { final HybridTextEditing implementation; /// Handles "flutter/textinput" platform messages received from the framework. - void handleTextInput(ByteData data) { - final MethodCall call = const JSONMethodCodec().decodeMethodCall(data); + void handleTextInput( + ByteData data, + ui.PlatformMessageResponseCallback callback) { + const JSONMethodCodec codec = JSONMethodCodec(); + final MethodCall call = codec.decodeMethodCall(data); switch (call.method) { case 'TextInput.setClient': implementation.setClient( @@ -815,6 +818,7 @@ class TextEditingChannel { default: throw StateError('Unsupported method call on the flutter/textinput channel: ${call.method}'); } + window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); } /// Sends the 'TextInputClient.updateEditingState' message to the framework. diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index e2004cda29286..8bfb18039b5fb 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -15,19 +15,11 @@ class EngineWindow extends ui.Window { } @override - double get devicePixelRatio { - if (_debugDevicePixelRatio != null) { - return _debugDevicePixelRatio; - } + double get devicePixelRatio => _debugDevicePixelRatio != null + ? _debugDevicePixelRatio + : browserDevicePixelRatio; - if (experimentalUseSkia) { - return browserDevicePixelRatio; - } else { - return 1.0; - } - } - - /// Returns device pixel ratio returns by browser. + /// Returns device pixel ratio returned by browser. static double get browserDevicePixelRatio { double ratio = html.window.devicePixelRatio; // Guard against WebOS returning 0. @@ -38,10 +30,7 @@ class EngineWindow extends ui.Window { /// /// This is useful in tests to emulate screens of different dimensions. void debugOverrideDevicePixelRatio(double value) { - assert(() { - _debugDevicePixelRatio = value; - return true; - }()); + _debugDevicePixelRatio = value; } double _debugDevicePixelRatio; @@ -160,26 +149,38 @@ class EngineWindow extends ui.Window { case 'HapticFeedback.vibrate': final String type = decoded.arguments; domRenderer.vibrate(_getHapticFeedbackDuration(type)); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setApplicationSwitcherDescription': final Map arguments = decoded.arguments; domRenderer.setTitle(arguments['label']); domRenderer.setThemeColor(ui.Color(arguments['primaryColor'])); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'SystemSound.play': // There are no default system sounds on web. + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'Clipboard.setData': ClipboardMessageHandler().setDataMethodCall(decoded, callback); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'Clipboard.getData': ClipboardMessageHandler().getDataMethodCall(callback); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; } break; case 'flutter/textinput': - textEditing.channel.handleTextInput(data); + textEditing.channel.handleTextInput(data, callback); + return; + + case 'flutter/web_test_e2e': + const MethodCodec codec = JSONMethodCodec(); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope( + _handleWebTestEnd2EndMessage(codec, data) + )); return; case 'flutter/platform_views': @@ -192,7 +193,9 @@ class EngineWindow extends ui.Window { case 'flutter/accessibility': // In widget tests we want to bypass processing of platform messages. - accessibilityAnnouncements.handleMessage(data); + final StandardMessageCodec codec = StandardMessageCodec(); + accessibilityAnnouncements.handleMessage(codec, data); + _replyToPlatformMessage(callback, codec.encodeMessage(true)); return; case 'flutter/navigation': @@ -204,9 +207,11 @@ class EngineWindow extends ui.Window { case 'routePushed': case 'routeReplaced': _browserHistory.setRouteName(message['routeName']); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); break; case 'routePopped': _browserHistory.setRouteName(message['previousRouteName']); + _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); break; } return; @@ -250,7 +255,9 @@ class EngineWindow extends ui.Window { ByteData data, ) { Future.delayed(Duration.zero).then((_) { - callback(data); + if (callback != null) { + callback(data); + } }); } @@ -315,6 +322,20 @@ class EngineWindow extends ui.Window { Rasterizer rasterizer = experimentalUseSkia ? Rasterizer(Surface()) : null; } +bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { + final MethodCall decoded = codec.decodeMethodCall(data); + final Map message = decoded.arguments; + double ratio = double.parse(decoded.arguments); + bool result = false; + switch(decoded.method) { + case 'setDevicePixelRatio': + window.debugOverrideDevicePixelRatio(ratio); + window.onMetricsChanged(); + return true; + } + return false; +} + /// The window singleton. /// /// `dart:ui` window delegates to this value. However, this value has a wider diff --git a/lib/web_ui/lib/src/ui/test_embedding.dart b/lib/web_ui/lib/src/ui/test_embedding.dart index 5952b19ed8c2f..471876706c0f4 100644 --- a/lib/web_ui/lib/src/ui/test_embedding.dart +++ b/lib/web_ui/lib/src/ui/test_embedding.dart @@ -29,7 +29,7 @@ Future ensureTestPlatformInitializedThenRunTest( /// are available. Future _platformInitializedFuture; -/// Initializes domRenderer with specific devicePixelRation and physicalSize. +/// Initializes domRenderer with specific devicePixelRatio and physicalSize. Future webOnlyInitializeTestDomRenderer({double devicePixelRatio = 3.0}) { // Force-initialize DomRenderer so it doesn't overwrite test pixel ratio. engine.domRenderer; diff --git a/lib/web_ui/test/engine/semantics/accessibility_test.dart b/lib/web_ui/test/engine/semantics/accessibility_test.dart index 8c572563c38db..767d4742ad0ea 100644 --- a/lib/web_ui/test/engine/semantics/accessibility_test.dart +++ b/lib/web_ui/test/engine/semantics/accessibility_test.dart @@ -33,7 +33,8 @@ void main() { // Initially there is no accessibility-element expect(document.getElementById('accessibility-element'), isNull); - accessibilityAnnouncements.handleMessage(codec.encodeMessage(testInput)); + accessibilityAnnouncements.handleMessage(codec, + codec.encodeMessage(testInput)); expect( document.getElementById('accessibility-element'), isNotNull, diff --git a/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart index e53d37d3ab080..fd7f45e6a0023 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_golden_test.dart @@ -222,12 +222,15 @@ void main() async { ); final SceneBuilder sb = SceneBuilder(); + sb.pushTransform(Matrix4.diagonal3Values(EngineWindow.browserDevicePixelRatio, + EngineWindow.browserDevicePixelRatio, 1.0).storage); sb.pushTransform(Matrix4.rotationZ(math.pi / 2).storage); sb.pushOffset(0, -500); sb.pushClipRect(canvasSize); sb.pop(); sb.pop(); sb.pop(); + sb.pop(); final SurfaceScene scene = sb.build(); final html.Element sceneElement = scene.webOnlyRootElement; diff --git a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart b/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart index 455bceb07551f..076fff0ff3853 100644 --- a/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/compositing_golden_test.dart @@ -405,6 +405,11 @@ void _testCullRectComputation() { // enough to fit the rotated clip. test('clips correctly when using 3d transforms', () async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + + builder.pushTransform(Matrix4.diagonal3Values( + EngineWindow.browserDevicePixelRatio, + EngineWindow.browserDevicePixelRatio, 1.0).storage); + // TODO(yjbanov): see the TODO below. // final double screenWidth = html.window.innerWidth.toDouble(); // final double screenHeight = html.window.innerHeight.toDouble(); @@ -499,6 +504,7 @@ void _testCullRectComputation() { builder.pop(); // pushClipRect builder.pop(); // pushOffset builder.pop(); // pushTransform scale + builder.pop(); // pushTransform scale devicepixelratio html.document.body.append(builder.build().webOnlyRootElement); await matchGoldenFile('compositing_3d_rotate1.png', region: region); diff --git a/lib/web_ui/test/text_editing_test.dart b/lib/web_ui/test/text_editing_test.dart index dad9bbdd80af7..97360e41357eb 100644 --- a/lib/web_ui/test/text_editing_test.dart +++ b/lib/web_ui/test/text_editing_test.dart @@ -560,7 +560,7 @@ void main() { /// Emulates sending of a message by the framework to the engine. void sendFrameworkMessage(dynamic message) { - textEditing.channel.handleTextInput(message); + textEditing.channel.handleTextInput(message, (ByteData data) {}); } /// Sends the necessary platform messages to activate a text field and show From 81133f2bcb69decc5d76208dea5ff02f7e734025 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 16:11:01 -0400 Subject: [PATCH 138/386] Roll src/third_party/skia f11e33152460..b6a99b7b9acb (5 commits) (#17323) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 932004192c90d..6004eb1388d8d 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f11e331524605268df714fa41f9a9046c3f08c3e', + 'skia_revision': 'b6a99b7b9acbbaae466fd73516edf9019834e5fc', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a40285cdb76c3..e96ec82915f35 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: b39e6f199638d78c07e950bfaf5d74a7 +Signature: d038349d8e5836ab069607729c429932 UNUSED LICENSES: @@ -3898,6 +3898,7 @@ FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DBackendContext.h FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DTypes.h FILE: ../../../third_party/skia/include/private/GrD3DTypesPriv.h FILE: ../../../third_party/skia/include/private/SkIDChangeListener.h +FILE: ../../../third_party/skia/modules/canvaskit/viewer_bindings.cpp FILE: ../../../third_party/skia/src/core/SkIDChangeListener.cpp FILE: ../../../third_party/skia/src/core/SkVM_fwd.h FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.cpp @@ -6370,6 +6371,7 @@ FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/example.html FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/extra.html FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/node.example.js FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/package.json +FILE: ../../../third_party/skia/modules/canvaskit/canvaskit/viewer.html FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/example.html FILE: ../../../third_party/skia/modules/pathkit/npm-asmjs/package.json FILE: ../../../third_party/skia/modules/pathkit/npm-wasm/example.html From fb841071c26ab05035e220aa790441e44904ea1b Mon Sep 17 00:00:00 2001 From: Drew Fisher Date: Wed, 25 Mar 2020 13:17:28 -0700 Subject: [PATCH 139/386] fuchsia: remove use of replace_as_executable (second try) (#17313) On Fuchsia, we can now get executable VMOs from trusted backing filesystems. This allows us to remove the use of replace_as_executable in favor of opening files with `fdio_open_fd_at` with the `OPEN_RIGHT_EXECUTABLE` flag and getting VMOs by calling `fdio_get_vmo_exec`. By moving the responsibility for executability into the filesystem, we are able to remove `deprecated-ambient-replace-as-executable` from component manifests for non-JIT runners (the JIT runners still call replace_as_executable in Dart's allocator). It wasn't abundantly clear whether .cmx files for tests were used purely in AOT runtime environments or also saw JIT usage, so I left those as-is. For context: this is a second attempt at #16690, which was reverted because it broke the Dart JIT runner. The primary difference is that this time around, we correctly handle absolute vs relative paths, depending on whether library loading bottoms out in `fdio_open_fd` or `fdio_open_fd_at`. I've added additional assertions to help ensure any new usages use the correct shape of path. Testing: I verified locally that the flutter product runner works on Astro, and also successfully ran the Dart JIT example test (which was the thing blocking the google3 roll with the previous attempt at this patchset). Co-authored-by: Drew Fisher --- .../dart_runner/dart_component_controller.cc | 4 +- .../fuchsia/dart_runner/dart_runner.cc | 4 +- .../goodbye_dart/meta/goodbye_dart_aot.cmx | 3 -- .../meta/dart_aot_product_runner.cmx | 1 - .../dart_runner/meta/dart_aot_runner.cmx | 1 - .../fuchsia/dart_runner/service_isolate.cc | 12 ++--- shell/platform/fuchsia/flutter/component.cc | 14 +----- .../meta/flutter_aot_product_runner.cmx | 1 - .../flutter/meta/flutter_aot_runner.cmx | 1 - shell/platform/fuchsia/flutter/runner.cc | 2 +- .../runtime/dart/utils/mapped_resource.cc | 48 ++++++++++--------- .../fuchsia/runtime/dart/utils/vmo.cc | 48 +++++++++++++++---- .../platform/fuchsia/runtime/dart/utils/vmo.h | 5 +- 13 files changed, 82 insertions(+), 62 deletions(-) diff --git a/shell/platform/fuchsia/dart_runner/dart_component_controller.cc b/shell/platform/fuchsia/dart_runner/dart_component_controller.cc index 02f412353e706..b558c9c171a0d 100644 --- a/shell/platform/fuchsia/dart_runner/dart_component_controller.cc +++ b/shell/platform/fuchsia/dart_runner/dart_component_controller.cc @@ -201,12 +201,12 @@ bool DartComponentController::SetupFromKernel() { } if (!dart_utils::MappedResource::LoadFromNamespace( - nullptr, "pkg/data/isolate_core_snapshot_data.bin", + nullptr, "/pkg/data/isolate_core_snapshot_data.bin", isolate_snapshot_data_)) { return false; } if (!dart_utils::MappedResource::LoadFromNamespace( - nullptr, "pkg/data/isolate_core_snapshot_instructions.bin", + nullptr, "/pkg/data/isolate_core_snapshot_instructions.bin", isolate_snapshot_instructions_, true /* executable */)) { return false; } diff --git a/shell/platform/fuchsia/dart_runner/dart_runner.cc b/shell/platform/fuchsia/dart_runner/dart_runner.cc index 74eb6b42401e8..7f72a824f1459 100644 --- a/shell/platform/fuchsia/dart_runner/dart_runner.cc +++ b/shell/platform/fuchsia/dart_runner/dart_runner.cc @@ -158,11 +158,11 @@ DartRunner::DartRunner() : context_(sys::ComponentContext::Create()) { params.vm_snapshot_instructions = ::_kDartVmSnapshotInstructions; #else if (!dart_utils::MappedResource::LoadFromNamespace( - nullptr, "pkg/data/vm_snapshot_data.bin", vm_snapshot_data_)) { + nullptr, "/pkg/data/vm_snapshot_data.bin", vm_snapshot_data_)) { FX_LOG(FATAL, LOG_TAG, "Failed to load vm snapshot data"); } if (!dart_utils::MappedResource::LoadFromNamespace( - nullptr, "pkg/data/vm_snapshot_instructions.bin", + nullptr, "/pkg/data/vm_snapshot_instructions.bin", vm_snapshot_instructions_, true /* executable */)) { FX_LOG(FATAL, LOG_TAG, "Failed to load vm snapshot instructions"); } diff --git a/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/meta/goodbye_dart_aot.cmx b/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/meta/goodbye_dart_aot.cmx index c6705a70ac564..e041f24109f72 100644 --- a/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/meta/goodbye_dart_aot.cmx +++ b/shell/platform/fuchsia/dart_runner/examples/goodbye_dart/meta/goodbye_dart_aot.cmx @@ -3,9 +3,6 @@ "data": "data/goodbye_dart_aot" }, "sandbox": { - "features": [ - "deprecated-ambient-replace-as-executable" - ], "services": [ "fuchsia.intl.PropertyProvider", "fuchsia.sys.Environment" diff --git a/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx b/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx index 629c352e01ae3..26c6db097e343 100644 --- a/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx +++ b/shell/platform/fuchsia/dart_runner/meta/dart_aot_product_runner.cmx @@ -5,7 +5,6 @@ "sandbox": { "features": [ "config-data", - "deprecated-ambient-replace-as-executable", "root-ssl-certificates" ], "services": [ diff --git a/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx b/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx index 629c352e01ae3..26c6db097e343 100644 --- a/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx +++ b/shell/platform/fuchsia/dart_runner/meta/dart_aot_runner.cmx @@ -5,7 +5,6 @@ "sandbox": { "features": [ "config-data", - "deprecated-ambient-replace-as-executable", "root-ssl-certificates" ], "services": [ diff --git a/shell/platform/fuchsia/dart_runner/service_isolate.cc b/shell/platform/fuchsia/dart_runner/service_isolate.cc index 6da8a3de47e43..4906a323bbd2d 100644 --- a/shell/platform/fuchsia/dart_runner/service_isolate.cc +++ b/shell/platform/fuchsia/dart_runner/service_isolate.cc @@ -82,7 +82,7 @@ Dart_Isolate CreateServiceIsolate(const char* uri, #if defined(AOT_RUNTIME) // The VM service was compiled as a separate app. - const char* snapshot_path = "pkg/data/vmservice_snapshot.so"; + const char* snapshot_path = "/pkg/data/vmservice_snapshot.so"; if (elf_snapshot.Load(nullptr, snapshot_path)) { vmservice_data = elf_snapshot.IsolateData(); vmservice_instructions = elf_snapshot.IsolateInstrs(); @@ -92,14 +92,14 @@ Dart_Isolate CreateServiceIsolate(const char* uri, } else { // The VM service was compiled as a separate app. const char* snapshot_data_path = - "pkg/data/vmservice_isolate_snapshot_data.bin"; + "/pkg/data/vmservice_isolate_snapshot_data.bin"; const char* snapshot_instructions_path = - "pkg/data/vmservice_isolate_snapshot_instructions.bin"; + "/pkg/data/vmservice_isolate_snapshot_instructions.bin"; #else // The VM service is embedded in the core snapshot. - const char* snapshot_data_path = "pkg/data/isolate_core_snapshot_data.bin"; + const char* snapshot_data_path = "/pkg/data/isolate_core_snapshot_data.bin"; const char* snapshot_instructions_path = - "pkg/data/isolate_core_snapshot_instructions.bin"; + "/pkg/data/isolate_core_snapshot_instructions.bin"; #endif if (!dart_utils::MappedResource::LoadFromNamespace( @@ -196,7 +196,7 @@ Dart_Isolate CreateServiceIsolate(const char* uri, Dart_Handle GetVMServiceAssetsArchiveCallback() { dart_utils::MappedResource observatory_tar; if (!dart_utils::MappedResource::LoadFromNamespace( - nullptr, "pkg/data/observatory.tar", observatory_tar)) { + nullptr, "/pkg/data/observatory.tar", observatory_tar)) { FX_LOG(ERROR, LOG_TAG, "Failed to load Observatory assets"); return nullptr; } diff --git a/shell/platform/fuchsia/flutter/component.cc b/shell/platform/fuchsia/flutter/component.cc index 9f6bc6f91596c..e0d58f3e80330 100644 --- a/shell/platform/fuchsia/flutter/component.cc +++ b/shell/platform/fuchsia/flutter/component.cc @@ -431,7 +431,8 @@ class FileInNamespaceBuffer final : public fml::Mapping { FileInNamespaceBuffer(int namespace_fd, const char* path, bool executable) : address_(nullptr), size_(0) { fuchsia::mem::Buffer buffer; - if (!dart_utils::VmoFromFilenameAt(namespace_fd, path, &buffer)) { + if (!dart_utils::VmoFromFilenameAt(namespace_fd, path, executable, + &buffer)) { return; } if (buffer.size == 0) { @@ -441,17 +442,6 @@ class FileInNamespaceBuffer final : public fml::Mapping { uint32_t flags = ZX_VM_PERM_READ; if (executable) { flags |= ZX_VM_PERM_EXECUTE; - - // VmoFromFilenameAt will return VMOs without ZX_RIGHT_EXECUTE, - // so we need replace_as_executable to be able to map them as - // ZX_VM_PERM_EXECUTE. - // TODO(mdempsky): Update comment once SEC-42 is fixed. - zx_status_t status = - buffer.vmo.replace_as_executable(zx::handle(), &buffer.vmo); - if (status != ZX_OK) { - FML_LOG(FATAL) << "Failed to make VMO executable: " - << zx_status_get_string(status); - } } uintptr_t addr; zx_status_t status = diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx index 019dc11340d84..914c22b6f2dbb 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_product_runner.cmx @@ -5,7 +5,6 @@ "sandbox": { "features": [ "config-data", - "deprecated-ambient-replace-as-executable", "root-ssl-certificates", "vulkan" ], diff --git a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx index af0c724590652..11ba40d7870f2 100644 --- a/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx +++ b/shell/platform/fuchsia/flutter/meta/flutter_aot_runner.cmx @@ -5,7 +5,6 @@ "sandbox": { "features": [ "config-data", - "deprecated-ambient-replace-as-executable", "root-ssl-certificates", "vulkan" ], diff --git a/shell/platform/fuchsia/flutter/runner.cc b/shell/platform/fuchsia/flutter/runner.cc index 2fe0b5626ada6..6459236730e16 100644 --- a/shell/platform/fuchsia/flutter/runner.cc +++ b/shell/platform/fuchsia/flutter/runner.cc @@ -87,7 +87,7 @@ bool InitializeICU() { const char* data_path = kIcuDataPath; fuchsia::mem::Buffer icu_data; - if (!dart_utils::VmoFromFilename(data_path, &icu_data)) { + if (!dart_utils::VmoFromFilename(data_path, false, &icu_data)) { return false; } diff --git a/shell/platform/fuchsia/runtime/dart/utils/mapped_resource.cc b/shell/platform/fuchsia/runtime/dart/utils/mapped_resource.cc index d8837e6fbc27f..0224cf25f714d 100644 --- a/shell/platform/fuchsia/runtime/dart/utils/mapped_resource.cc +++ b/shell/platform/fuchsia/runtime/dart/utils/mapped_resource.cc @@ -32,41 +32,31 @@ static bool OpenVmo(fuchsia::mem::Buffer* resource_vmo, bool executable) { TRACE_DURATION("dart", "LoadFromNamespace", "path", path); - // openat of a path with a leading '/' ignores the namespace fd. - dart_utils::Check(path[0] != '/', LOG_TAG); - if (namespc == nullptr) { - if (!VmoFromFilename(path, resource_vmo)) { + // Opening a file in the root namespace expects an absolute path. + dart_utils::Check(path[0] == '/', LOG_TAG); + if (!VmoFromFilename(path, executable, resource_vmo)) { return false; } } else { + // openat of a path with a leading '/' ignores the namespace fd. + // require a relative path. + dart_utils::Check(path[0] != '/', LOG_TAG); + auto root_dir = fdio_ns_opendir(namespc); if (root_dir < 0) { FX_LOG(ERROR, LOG_TAG, "Failed to open namespace directory"); return false; } - bool result = dart_utils::VmoFromFilenameAt(root_dir, path, resource_vmo); + bool result = + dart_utils::VmoFromFilenameAt(root_dir, path, executable, resource_vmo); close(root_dir); if (!result) { return result; } } - if (executable) { - // VmoFromFilenameAt will return VMOs without ZX_RIGHT_EXECUTE, - // so we need replace_as_executable to be able to map them as - // ZX_VM_PERM_EXECUTE. - // TODO(mdempsky): Update comment once SEC-42 is fixed. - zx_status_t status = resource_vmo->vmo.replace_as_executable( - zx::handle(), &resource_vmo->vmo); - if (status != ZX_OK) { - FX_LOGF(ERROR, LOG_TAG, "Failed to make VMO executable: %s", - zx_status_get_string(status)); - return false; - } - } - return true; } @@ -114,10 +104,22 @@ MappedResource::~MappedResource() { static int OpenFdExec(const std::string& path, int dirfd) { int fd = -1; - zx_status_t result = fdio_open_fd_at( - dirfd, path.c_str(), - fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_EXECUTABLE, - &fd); + zx_status_t result; + if (dirfd == AT_FDCWD) { + // fdio_open_fd_at does not support AT_FDCWD, by design. Use fdio_open_fd + // and expect an absolute path for that usage pattern. + dart_utils::Check(path[0] == '/', LOG_TAG); + result = fdio_open_fd( + path.c_str(), + fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_EXECUTABLE, + &fd); + } else { + dart_utils::Check(path[0] != '/', LOG_TAG); + result = fdio_open_fd_at( + dirfd, path.c_str(), + fuchsia::io::OPEN_RIGHT_READABLE | fuchsia::io::OPEN_RIGHT_EXECUTABLE, + &fd); + } if (result != ZX_OK) { FX_LOGF(ERROR, LOG_TAG, "fdio_open_fd_at(%s) failed: %s", path.c_str(), zx_status_get_string(result)); diff --git a/shell/platform/fuchsia/runtime/dart/utils/vmo.cc b/shell/platform/fuchsia/runtime/dart/utils/vmo.cc index 130181c777bce..87f181e38b49c 100644 --- a/shell/platform/fuchsia/runtime/dart/utils/vmo.cc +++ b/shell/platform/fuchsia/runtime/dart/utils/vmo.cc @@ -7,15 +7,18 @@ #include #include +#include #include +#include #include #include +#include #include "runtime/dart/utils/logging.h" namespace { -bool VmoFromFd(int fd, fuchsia::mem::Buffer* buffer) { +bool VmoFromFd(int fd, bool executable, fuchsia::mem::Buffer* buffer) { if (!buffer) { FX_LOG(FATAL, LOG_TAG, "Invalid buffer pointer"); } @@ -27,7 +30,14 @@ bool VmoFromFd(int fd, fuchsia::mem::Buffer* buffer) { } zx_handle_t result = ZX_HANDLE_INVALID; - if (fdio_get_vmo_copy(fd, &result) != ZX_OK) { + zx_status_t status; + if (executable) { + status = fdio_get_vmo_exec(fd, &result); + } else { + status = fdio_get_vmo_copy(fd, &result); + } + + if (status != ZX_OK) { return false; } @@ -42,20 +52,42 @@ bool VmoFromFd(int fd, fuchsia::mem::Buffer* buffer) { namespace dart_utils { bool VmoFromFilename(const std::string& filename, + bool executable, fuchsia::mem::Buffer* buffer) { - return VmoFromFilenameAt(AT_FDCWD, filename, buffer); + // Note: the implementation here cannot be shared with VmoFromFilenameAt + // because fdio_open_fd_at does not aim to provide POSIX compatibility, and + // thus does not handle AT_FDCWD as dirfd. + uint32_t flags = fuchsia::io::OPEN_RIGHT_READABLE | + (executable ? fuchsia::io::OPEN_RIGHT_EXECUTABLE : 0); + zx_status_t status; + int fd; + + status = fdio_open_fd(filename.c_str(), flags, &fd); + if (status != ZX_OK) { + FX_LOGF(ERROR, LOG_TAG, "fdio_open_fd(\"%s\", %08x) failed: %s", + filename.c_str(), flags, zx_status_get_string(status)); + return false; + } + bool result = VmoFromFd(fd, executable, buffer); + close(fd); + return result; } bool VmoFromFilenameAt(int dirfd, const std::string& filename, + bool executable, fuchsia::mem::Buffer* buffer) { - int fd = openat(dirfd, filename.c_str(), O_RDONLY); - if (fd == -1) { - FX_LOGF(ERROR, LOG_TAG, "openat(\"%s\") failed: %s", filename.c_str(), - strerror(errno)); + uint32_t flags = fuchsia::io::OPEN_RIGHT_READABLE | + (executable ? fuchsia::io::OPEN_RIGHT_EXECUTABLE : 0); + zx_status_t status; + int fd; + status = fdio_open_fd_at(dirfd, filename.c_str(), flags, &fd); + if (status != ZX_OK) { + FX_LOGF(ERROR, LOG_TAG, "fdio_open_fd_at(%d, \"%s\", %08x) failed: %s", + dirfd, filename.c_str(), flags, zx_status_get_string(status)); return false; } - bool result = VmoFromFd(fd, buffer); + bool result = VmoFromFd(fd, executable, buffer); close(fd); return result; } diff --git a/shell/platform/fuchsia/runtime/dart/utils/vmo.h b/shell/platform/fuchsia/runtime/dart/utils/vmo.h index bc4065eb815e4..52871d2e32751 100644 --- a/shell/platform/fuchsia/runtime/dart/utils/vmo.h +++ b/shell/platform/fuchsia/runtime/dart/utils/vmo.h @@ -11,10 +11,13 @@ namespace dart_utils { -bool VmoFromFilename(const std::string& filename, fuchsia::mem::Buffer* buffer); +bool VmoFromFilename(const std::string& filename, + bool executable, + fuchsia::mem::Buffer* buffer); bool VmoFromFilenameAt(int dirfd, const std::string& filename, + bool executable, fuchsia::mem::Buffer* buffer); zx_status_t IsSizeValid(const fuchsia::mem::Buffer& buffer, bool* is_valid); From f1008d9bf99ef43cd48b21913b5663552035b35f Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Wed, 25 Mar 2020 13:37:27 -0700 Subject: [PATCH 140/386] Allow drawing raster cache results whose device rect is one pixel larger than the cached image (#17278) RasterCacheResult::draw constructs the device target rectangle by calling SkRect::roundOut, which rounds down the left/top coordinates and rounds up the right/bottom coordinates. The rounding can produce a device rect whose width and/or height differs from the cache result image's width/height by one pixel. --- flow/raster_cache.cc | 4 +++- flow/raster_cache_unittests.cc | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/flow/raster_cache.cc b/flow/raster_cache.cc index f179905ed8e53..e7709b14c7bf0 100644 --- a/flow/raster_cache.cc +++ b/flow/raster_cache.cc @@ -26,7 +26,9 @@ void RasterCacheResult::draw(SkCanvas& canvas, const SkPaint* paint) const { SkAutoCanvasRestore auto_restore(&canvas, true); SkIRect bounds = RasterCache::GetDeviceBounds(logical_rect_, canvas.getTotalMatrix()); - FML_DCHECK(bounds.size() == image_->dimensions()); + FML_DCHECK( + std::abs(bounds.size().width() - image_->dimensions().width()) <= 1 && + std::abs(bounds.size().height() - image_->dimensions().height()) <= 1); canvas.resetMatrix(); canvas.drawImage(image_, bounds.fLeft, bounds.fTop, paint); } diff --git a/flow/raster_cache_unittests.cc b/flow/raster_cache_unittests.cc index 03e3b3879c469..0c00a47087f40 100644 --- a/flow/raster_cache_unittests.cc +++ b/flow/raster_cache_unittests.cc @@ -122,5 +122,37 @@ TEST(RasterCache, SweepsRemoveUnusedFrames) { ASSERT_FALSE(cache.Get(*picture, matrix).is_valid()); } +// Construct a cache result whose device target rectangle rounds out to be one +// pixel wider than the cached image. Verify that it can be drawn without +// triggering any assertions. +TEST(RasterCache, DeviceRectRoundOut) { + size_t threshold = 1; + flutter::RasterCache cache(threshold); + + SkPictureRecorder recorder; + SkRect logical_rect = SkRect::MakeLTRB(28, 0, 354.56731, 310.288); + recorder.beginRecording(logical_rect); + SkPaint paint; + paint.setColor(SK_ColorRED); + recorder.getRecordingCanvas()->drawRect(logical_rect, paint); + sk_sp picture = recorder.finishRecordingAsPicture(); + + SkMatrix ctm = SkMatrix::MakeAll(1.3312, 0, 233, 0, 1.3312, 206, 0, 0, 1); + + sk_sp srgb = SkColorSpace::MakeSRGB(); + ASSERT_FALSE( + cache.Prepare(NULL, picture.get(), ctm, srgb.get(), true, false)); + ASSERT_FALSE(cache.Get(*picture, ctm).is_valid()); + cache.SweepAfterFrame(); + ASSERT_TRUE(cache.Prepare(NULL, picture.get(), ctm, srgb.get(), true, false)); + ASSERT_TRUE(cache.Get(*picture, ctm).is_valid()); + + SkCanvas canvas(100, 100, nullptr); + canvas.setMatrix(ctm); + canvas.translate(248, 0); + + cache.Get(*picture, ctm).draw(canvas); +} + } // namespace testing } // namespace flutter From b235233e9d396ed240f09ce687ee7d51b72dfce1 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Wed, 25 Mar 2020 14:36:10 -0700 Subject: [PATCH 141/386] Revert unobstructed platform views (#17326) --- flow/embedded_views.cc | 5 +- flow/embedded_views.h | 5 +- flow/layers/picture_layer.cc | 2 +- flow/layers/picture_layer_unittests.cc | 3 + shell/common/rasterizer.cc | 12 +- .../framework/Source/FlutterPlatformViews.mm | 321 ++++++------------ .../Source/FlutterPlatformViews_Internal.h | 87 +---- .../Source/FlutterPlatformViews_Internal.mm | 3 +- shell/platform/darwin/ios/ios_surface.h | 5 +- shell/platform/darwin/ios/ios_surface.mm | 12 +- .../embedder_external_view_embedder.cc | 6 +- .../embedder_external_view_embedder.h | 5 +- .../Scenarios.xcodeproj/project.pbxproj | 4 - .../ios/Scenarios/Scenarios/AppDelegate.m | 7 - .../PlatformViewGestureRecognizerTests.m | 6 +- .../UnobstructedPlatformViewTests.m | 254 -------------- ...tform_view_clippath_iPhone 8_simulator.png | Bin 20295 -> 30022 bytes ...form_view_transform_iPhone 8_simulator.png | Bin 22360 -> 33727 bytes testing/scenario_app/lib/main.dart | 6 - .../scenario_app/lib/src/platform_view.dart | 227 +------------ 20 files changed, 126 insertions(+), 844 deletions(-) delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index 5234bf1e50c8c..c660f4691318b 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -6,13 +6,10 @@ namespace flutter { -bool ExternalViewEmbedder::SubmitFrame(GrContext* context, - SkCanvas* background_canvas) { +bool ExternalViewEmbedder::SubmitFrame(GrContext* context) { return false; }; -void ExternalViewEmbedder::FinishFrame(){}; - void MutatorsStack::PushClipRect(const SkRect& rect) { std::shared_ptr element = std::make_shared(rect); vector_.push_back(element); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 7a491a8a152ef..030eb88c8a06d 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -248,10 +248,7 @@ class ExternalViewEmbedder { // Must be called on the UI thread. virtual SkCanvas* CompositeEmbeddedView(int view_id) = 0; - virtual bool SubmitFrame(GrContext* context, SkCanvas* background_canvas); - - // This is called after submitting the embedder frame and the surface frame. - virtual void FinishFrame(); + virtual bool SubmitFrame(GrContext* context); FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 08c09cc9e833b..3bc7e394c1033 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -59,7 +59,7 @@ void PictureLayer::Paint(PaintContext& context) const { return; } } - picture()->playback(context.leaf_nodes_canvas); + context.leaf_nodes_canvas->drawPicture(picture()); } } // namespace flutter diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 4f565cf500ecc..687c870eeac66 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -94,6 +94,9 @@ TEST_F(PictureLayerTest, SimplePicture) { 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( layer_offset_matrix)}}, #endif + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), + SkMatrix()}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f7e4350fe9c97..811898b5a42d2 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -342,17 +342,9 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { if (raster_status == RasterStatus::kFailed) { return raster_status; } + frame->Submit(); if (external_view_embedder != nullptr) { - external_view_embedder->SubmitFrame(surface_->GetContext(), - root_surface_canvas); - // The external view embedder may mutate the root surface canvas while - // submitting the frame. - // Therefore, submit the final frame after asking the external view - // embedder to submit the frame. - frame->Submit(); - external_view_embedder->FinishFrame(); - } else { - frame->Submit(); + external_view_embedder->SubmitFrame(surface_->GetContext()); } FireNextFrameCallbackIfPresent(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 32c91c91e4077..b86c4623fa7a7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -8,77 +8,16 @@ #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/ios_surface_gl.h" -#include #include #include #include #include "FlutterPlatformViews_Internal.h" -#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" namespace flutter { -std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( - GrContext* gr_context, - std::shared_ptr ios_context) { - if (available_layer_index_ >= layers_.size()) { - std::shared_ptr layer; - - if (!gr_context) { - fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - - layer = std::make_shared( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - } else { - CGFloat screenScale = [UIScreen mainScreen].scale; - fml::scoped_nsobject overlay_view( - [[FlutterOverlayView alloc] initWithContentsScale:screenScale]); - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - - layer = std::make_shared( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - layer->gr_context = gr_context; - } - layers_.push_back(layer); - } - auto layer = layers_[available_layer_index_]; - if (gr_context != layer->gr_context) { - layer->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = layer->ios_surface.get(); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - layer->surface = std::move(surface); - } - available_layer_index_++; - return layer; -} - -void FlutterPlatformViewLayerPool::RecycleLayers() { - available_layer_index_ = 0; -} - -std::vector> -FlutterPlatformViewLayerPool::GetUnusedLayers() { - std::vector> results; - for (size_t i = available_layer_index_; i < layers_.size(); i++) { - results.push_back(layers_[i]); - } - return results; -} - void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { flutter_view_.reset([flutter_view retain]); } @@ -144,9 +83,6 @@ NSObject* embedded_view = [factory createWithFrame:CGRectZero viewIdentifier:viewId arguments:params]; - // Set a unique view identifier, so the platform view can be identified in unit tests. - [embedded_view view].accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%ld]", viewId]; views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] @@ -260,11 +196,8 @@ int view_id, std::unique_ptr params) { picture_recorders_[view_id] = std::make_unique(); - - auto rtree_factory = RTreeFactory(); - platform_view_rtrees_[view_id] = rtree_factory.getInstance(); - picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_), &rtree_factory); - + picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_)); + picture_recorders_[view_id]->getRecordingCanvas()->clear(SK_ColorTRANSPARENT); composition_order_.push_back(view_id); if (current_composition_params_.count(view_id) == 1 && @@ -428,185 +361,77 @@ composition_order_.clear(); active_composition_order_.clear(); picture_recorders_.clear(); - platform_view_rtrees_.clear(); current_composition_params_.clear(); clip_count_.clear(); views_to_recomposite_.clear(); - layer_pool_->RecycleLayers(); -} - -SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) { - UIView* platform_view = [views_[view_id].get() view]; - UIScreen* screen = [UIScreen mainScreen]; - CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds - toView:flutter_view_]; - return SkRect::MakeXYWH(platform_view_cgrect.origin.x * screen.scale, // - platform_view_cgrect.origin.y * screen.scale, // - platform_view_cgrect.size.width * screen.scale, // - platform_view_cgrect.size.height * screen.scale // - ); } bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context, - SkCanvas* background_canvas) { + std::shared_ptr ios_context) { DisposeViews(); - // Resolve all pending GPU operations before allocating a new surface. - background_canvas->flush(); - // Clipping the background canvas before drawing the picture recorders requires to - // save and restore the clip context. - SkAutoCanvasRestore save(background_canvas, /*doSave=*/true); - // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. - LayersMap platform_view_layers; - - auto did_submit = true; - auto num_platform_views = composition_order_.size(); - - for (size_t i = 0; i < num_platform_views; i++) { - int64_t platform_view_id = composition_order_[i]; - sk_sp rtree = platform_view_rtrees_[platform_view_id]; - sk_sp picture = picture_recorders_[platform_view_id]->finishRecordingAsPicture(); - - // Check if the current picture contains overlays that intersect with the - // current platform view or any of the previous platform views. - for (size_t j = i + 1; j > 0; j--) { - int64_t current_platform_view_id = composition_order_[j - 1]; - SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); - std::list intersection_rects = - rtree->searchNonOverlappingDrawnRects(platform_view_rect); - auto allocation_size = intersection_rects.size(); - - // For testing purposes, the overlay id is used to find the overlay view. - // This is the index of the layer for the current platform view. - auto overlay_id = platform_view_layers[current_platform_view_id].size(); - - // If the max number of allocations per platform view is exceeded, - // then join all the rects into a single one. - // - // TODO(egarciad): Consider making this configurable. - // https://github.com/flutter/flutter/issues/52510 - if (allocation_size > kMaxLayerAllocations) { - SkRect joined_rect; - for (const SkRect& rect : intersection_rects) { - joined_rect.join(rect); - } - // Replace the rects in the intersection rects list for a single rect that is - // the union of all the rects in the list. - intersection_rects.clear(); - intersection_rects.push_back(joined_rect); - } - for (SkRect& joined_rect : intersection_rects) { - // Get the intersection rect between the current rect - // and the platform view rect. - joined_rect.intersect(platform_view_rect); - // Clip the background canvas, so it doesn't contain any of the pixels drawn - // on the overlay layer. - background_canvas->clipRect(joined_rect, SkClipOp::kDifference); - // Get a new host layer. - auto layer = GetLayer(gr_context, // - ios_context, // - picture, // - joined_rect, // - current_platform_view_id, // - overlay_id // - ); - did_submit &= layer->did_submit_last_frame; - platform_view_layers[current_platform_view_id].push_back(layer); - overlay_id++; - } + bool did_submit = true; + for (int64_t view_id : composition_order_) { + EnsureOverlayInitialized(view_id, ios_context, gr_context); + auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); + // If frame is null, AcquireFrame already printed out an error message. + if (frame) { + SkCanvas* canvas = frame->SkiaCanvas(); + canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); + canvas->flush(); + did_submit &= frame->Submit(); } - background_canvas->drawPicture(picture); - } - // If a layer was allocated in the previous frame, but it's not used in the current frame, - // then it can be removed from the scene. - RemoveUnusedLayers(); - // Organize the layers by their z indexes. - BringLayersIntoView(platform_view_layers); - // Mark all layers as available, so they can be used in the next frame. - layer_pool_->RecycleLayers(); - // Reset the composition order, so next frame starts empty. - composition_order_.clear(); - - return did_submit; -} - -void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { + } + picture_recorders_.clear(); + if (composition_order_ == active_composition_order_) { + composition_order_.clear(); + return did_submit; + } + DetachUnusedLayers(); + active_composition_order_.clear(); UIView* flutter_view = flutter_view_.get(); - auto zIndex = 0; - for (size_t i = 0; i < composition_order_.size(); i++) { - int64_t platform_view_id = composition_order_[i]; - std::vector> layers = layer_map[platform_view_id]; - UIView* platform_view_root = root_views_[platform_view_id].get(); - if (platform_view_root.superview != flutter_view) { - [flutter_view addSubview:platform_view_root]; + for (size_t i = 0; i < composition_order_.size(); i++) { + int view_id = composition_order_[i]; + // We added a chain of super views to the platform view to handle clipping. + // The `platform_view_root` is the view at the top of the chain which is a direct subview of the + // `FlutterView`. + UIView* platform_view_root = root_views_[view_id].get(); + UIView* overlay = overlays_[view_id]->overlay_view; + FML_CHECK(platform_view_root.superview == overlay.superview); + if (platform_view_root.superview == flutter_view) { + [flutter_view bringSubviewToFront:platform_view_root]; + [flutter_view bringSubviewToFront:overlay]; } else { - platform_view_root.layer.zPosition = zIndex++; - } - for (const std::shared_ptr& layer : layers) { - if ([layer->overlay_view superview] != flutter_view) { - [flutter_view addSubview:layer->overlay_view]; - } else { - layer->overlay_view.get().layer.zPosition = zIndex++; - } + [flutter_view addSubview:platform_view_root]; + [flutter_view addSubview:overlay]; + overlay.frame = flutter_view.bounds; } - active_composition_order_.push_back(platform_view_id); - } -} -std::shared_ptr FlutterPlatformViewsController::GetLayer( - GrContext* gr_context, - std::shared_ptr ios_context, - sk_sp picture, - SkRect rect, - int64_t view_id, - int64_t overlay_id) { - auto layer = layer_pool_->GetLayer(gr_context, ios_context); - auto screenScale = [UIScreen mainScreen].scale; - // Set the size of the overlay UIView. - layer->overlay_view.get().frame = CGRectMake(rect.x() / screenScale, // - rect.y() / screenScale, // - rect.width() / screenScale, // - rect.height() / screenScale // - ); - // Set a unique view identifier, so the overlay can be identified in unit tests. - layer->overlay_view.get().accessibilityIdentifier = - [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; - - std::unique_ptr frame = - layer->surface->AcquireFrame(SkISize::Make(rect.width(), rect.height())); - // If frame is null, AcquireFrame already printed out an error message. - if (!frame) { - return layer; - } - auto overlay_canvas = frame->SkiaCanvas(); - overlay_canvas->clear(SK_ColorTRANSPARENT); - // Offset the picture since its absolute position on the scene is determined - // by the position of the overlay view. - overlay_canvas->translate(-rect.x(), -rect.y()); - overlay_canvas->drawPicture(picture); - - layer->did_submit_last_frame = frame->Submit(); - return layer; -} - -void FlutterPlatformViewsController::RemoveUnusedLayers() { - auto layers = layer_pool_->GetUnusedLayers(); - for (const std::shared_ptr& layer : layers) { - [layer->overlay_view removeFromSuperview]; + active_composition_order_.push_back(view_id); } + composition_order_.clear(); + return did_submit; +} +void FlutterPlatformViewsController::DetachUnusedLayers() { std::unordered_set composition_order_set; for (int64_t view_id : composition_order_) { composition_order_set.insert(view_id); } - // Remove unused platform views. + for (int64_t view_id : active_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { + if (root_views_.find(view_id) == root_views_.end()) { + continue; + } + // We added a chain of super views to the platform view to handle clipping. + // The `platform_view_root` is the view at the top of the chain which is a direct subview of + // the `FlutterView`. UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; + [overlays_[view_id]->overlay_view.get() removeFromSuperview]; } } } @@ -633,6 +458,56 @@ views_to_dispose_.clear(); } +void FlutterPlatformViewsController::EnsureOverlayInitialized( + int64_t overlay_id, + std::shared_ptr ios_context, + GrContext* gr_context) { + FML_DCHECK(flutter_view_); + + auto overlay_it = overlays_.find(overlay_id); + + if (!gr_context) { + if (overlays_.count(overlay_id) != 0) { + return; + } + fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); + overlay_view.get().frame = flutter_view_.get().bounds; + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + overlays_[overlay_id] = std::make_unique( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + return; + } + + if (overlay_it != overlays_.end()) { + FlutterPlatformViewLayer* overlay = overlay_it->second.get(); + if (gr_context != overlay->gr_context) { + overlay->gr_context = gr_context; + // The overlay already exists, but the GrContext was changed so we need to recreate + // the rendering surface with the new GrContext. + IOSSurface* ios_surface = overlay_it->second->ios_surface.get(); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + overlay_it->second->surface = std::move(surface); + } + return; + } + auto contentsScale = flutter_view_.get().layer.contentsScale; + fml::scoped_nsobject overlay_view( + [[FlutterOverlayView alloc] initWithContentsScale:contentsScale]); + overlay_view.get().frame = flutter_view_.get().bounds; + overlay_view.get().autoresizingMask = + (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + overlays_[overlay_id] = std::make_unique( + std::move(overlay_view), std::move(ios_surface), std::move(surface)); + overlays_[overlay_id]->gr_context = gr_context; +} + } // namespace flutter // This recognizers delays touch events from being dispatched to the responder chain until it failed diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 59dc940c4d126..d135d7d2ac290 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,7 +6,6 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #include "flutter/flow/embedded_views.h" -#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" @@ -69,52 +68,12 @@ struct FlutterPlatformViewLayer { std::unique_ptr ios_surface; std::unique_ptr surface; - // Whether a frame for this layer was submitted. - bool did_submit_last_frame; - // The GrContext that is currently used by the overlay surfaces. // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrContext* gr_context; }; -// This class isn't thread safe. -class FlutterPlatformViewLayerPool { - public: - FlutterPlatformViewLayerPool() = default; - ~FlutterPlatformViewLayerPool() = default; - - // Gets a layer from the pool if available, or allocates a new one. - // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. - std::shared_ptr GetLayer(GrContext* gr_context, - std::shared_ptr ios_context); - - // Gets the layers in the pool that aren't currently used. - // This method doesn't mark the layers as unused. - std::vector> GetUnusedLayers(); - - // Marks the layers in the pool as available for reuse. - void RecycleLayers(); - - private: - // The index of the entry in the layers_ vector that determines the beginning of the unused - // layers. For example, consider the following vector: - // _____ - // | 0 | - /// |---| - /// | 1 | <-- available_layer_index_ - /// |---| - /// | 2 | - /// |---| - /// - /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 - /// cannot be reused. - size_t available_layer_index_ = 0; - std::vector> layers_; - - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); -}; - class FlutterPlatformViewsController { public: FlutterPlatformViewsController(); @@ -150,37 +109,14 @@ class FlutterPlatformViewsController { SkCanvas* CompositeEmbeddedView(int view_id); - // The rect of the platform view at index view_id. This rect has been translated into the - // host view coordinate system. Units are device screen pixels. - SkRect GetPlatformViewRect(int view_id); - // Discards all platform views instances and auxiliary resources. void Reset(); - bool SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context, - SkCanvas* background_canvas); + bool SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: - static const size_t kMaxLayerAllocations = 2; - - using LayersMap = std::map>>; - - // The pool of reusable view layers. The pool allows to recycle layer in each frame. - std::unique_ptr layer_pool_; - - // The platform view's R-tree keyed off the view id, which contains any subsequent - // draw operation until the next platform view or the last leaf node in the layer tree. - // - // The R-trees are deleted by the FlutterPlatformViewsController.reset(). - std::map> platform_view_rtrees_; - - // The platform view's picture recorder keyed off the view id, which contains any subsequent - // operation until the next platform view or the end of the last leaf node in the layer tree. - std::map> picture_recorders_; - fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; fml::scoped_nsobject flutter_view_controller_; @@ -227,12 +163,19 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies; + std::map> picture_recorders_; + void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); + + void DetachUnusedLayers(); // Dispose the views in `views_to_dispose_`. void DisposeViews(); + void EnsureOverlayInitialized(int64_t overlay_id, + std::shared_ptr ios_context, + GrContext* gr_context); // This will return true after pre-roll if any of the embedded views // have mutated for last layer tree. @@ -272,20 +215,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from - // the picture on the layer's canvas. - std::shared_ptr GetLayer(GrContext* gr_context, - std::shared_ptr ios_context, - sk_sp picture, - SkRect rect, - int64_t view_id, - int64_t overlay_id); - // Removes overlay views and platform views that aren't needed in the current frame. - void RemoveUnusedLayers(); - // Appends the overlay views and platform view and sets their z index based on the composition - // order. - void BringLayersIntoView(LayersMap layer_map); - FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index 21db9530fe0bb..9310fa1803f11 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -20,8 +20,7 @@ FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() - : layer_pool_(std::make_unique()){}; +FlutterPlatformViewsController::FlutterPlatformViewsController() = default; FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 8cba9285cfb02..58233d8a2f656 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -77,10 +77,7 @@ class IOSSurface : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; - - // |ExternalViewEmbedder| - void FinishFrame() override; + bool SubmitFrame(GrContext* context) override; public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index f51160e94e471..fe85c77f12c2d 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -132,18 +132,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { } // |ExternalViewEmbedder| -bool IOSSurface::SubmitFrame(GrContext* context, SkCanvas* background_canvas) { +bool IOSSurface::SubmitFrame(GrContext* context) { TRACE_EVENT0("flutter", "IOSSurface::SubmitFrame"); FML_CHECK(platform_views_controller_ != nullptr); - bool submitted = - platform_views_controller_->SubmitFrame(std::move(context), ios_context_, background_canvas); - return submitted; -} - -// |ExternalViewEmbedder| -void IOSSurface::FinishFrame() { - TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + bool submitted = platform_views_controller_->SubmitFrame(std::move(context), ios_context_); [CATransaction commit]; + return submitted; } } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 23658888f7caa..5e77073e7ff47 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -129,8 +129,7 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( } // |ExternalViewEmbedder| -bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, - SkCanvas* background_canvas) { +bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -266,7 +265,4 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, return true; } -// |ExternalViewEmbedder| -void EmbedderExternalViewEmbedder::FinishFrame() {} - } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 63c944a88d7ef..7000d2cde04cd 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -89,10 +89,7 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; - - // |ExternalViewEmbedder| - void FinishFrame() override; + bool SubmitFrame(GrContext* context) override; // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index 818d902b3e2e9..c24333a3a8a7f 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -43,7 +43,6 @@ 3DEF491A23C3BE6500184216 /* golden_platform_view_transform_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */; }; 59A97FD8236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */; }; 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */; }; - 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */; }; 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */; }; @@ -150,7 +149,6 @@ 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone 8_simulator.png"; sourceTree = ""; }; 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_iPhone SE_simulator.png"; sourceTree = ""; }; 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png"; sourceTree = ""; }; - 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnobstructedPlatformViewTests.m; sourceTree = ""; }; 6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenPlatformViewTests.h; sourceTree = ""; }; 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenPlatformViewTests.m; sourceTree = ""; }; 6816DB9F2317573300A51400 /* GoldenImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenImage.h; sourceTree = ""; }; @@ -247,7 +245,6 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( - 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */, 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 3DE09E8B23C010BC006C9851 /* golden_platform_view_clippath_iPhone 8_simulator.png */, 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */, @@ -491,7 +488,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 9bd732f647039..348889b19b856 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -29,13 +29,6 @@ - (BOOL)application:(UIApplication*)application // the launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. NSDictionary* launchArgsMap = @{ @"--platform-view" : @"platform_view", - @"--platform-view-no-overlay-intersection" : @"platform_view_no_overlay_intersection", - @"--platform-view-two-intersecting-overlays" : @"platform_view_two_intersecting_overlays", - @"--platform-view-partial-intersection" : @"platform_view_partial_intersection", - @"--platform-view-one-overlay-two-intersecting-overlays" : - @"platform_view_one_overlay_two_intersecting_overlays", - @"--platform-view-multiple-without-overlays" : @"platform_view_multiple_without_overlays", - @"--platform-view-max-overlays" : @"platform_view_max_overlays", @"--platform-view-multiple" : @"platform_view_multiple", @"--platform-view-multiple-background-foreground" : @"platform_view_multiple_background_foreground", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m index 3d583e1d5e824..d791210f22707 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -25,7 +25,7 @@ - (void)testRejectPolicyUtilTouchesEnded { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier hasPrefix:@"platform_view"]; + return [element.identifier isEqualToString:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { @@ -56,7 +56,7 @@ - (void)testRejectPolicyEager { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier hasPrefix:@"platform_view"]; + return [element.identifier isEqualToString:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { @@ -91,7 +91,7 @@ - (void)testAccept { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier hasPrefix:@"platform_view"]; + return [element.identifier isEqualToString:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m deleted file mode 100644 index 02e7eee35f098..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import - -@interface UnobstructedPlatformViewTests : XCTestCase - -@end - -@implementation UnobstructedPlatformViewTests - -- (void)setUp { - self.continueAfterFailure = NO; -} - -// A is the layer, which z index is higher than the platform view. -// +--------+ -// | PV | +---+ -// +--------+ | A | -// +---+ -- (void)testNoOverlay { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-no-overlay-intersection" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertFalse(overlay.exists); -} - -// A is the layer above the platform view. -// +-----------------+ -// | PV +---+ | -// | | A | | -// | +---+ | -// +-----------------+ -- (void)testOneOverlay { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 150); - XCTAssertEqual(overlay.frame.origin.y, 150); - XCTAssertEqual(overlay.frame.size.width, 50); - XCTAssertEqual(overlay.frame.size.height, 50); -} - -// A is the layer above the platform view. -// +-----------------+ -// | PV +---+ | -// +-----------| A |-+ -// +---+ -- (void)testOneOverlayPartialIntersection { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-partial-intersection" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 200); - XCTAssertEqual(overlay.frame.origin.y, 250); - XCTAssertEqual(overlay.frame.size.width, 50); - // Half the height of the overlay. - XCTAssertEqual(overlay.frame.size.height, 25); -} - -// A and B are the layers above the platform view. -// +--------------------+ -// | PV +------------+ | -// | | B +-----+ | | -// | +---| A |-+ | -// +----------| |---+ -// +-----+ -- (void)testTwoIntersectingOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-two-intersecting-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 150); - XCTAssertEqual(overlay.frame.origin.y, 150); - XCTAssertEqual(overlay.frame.size.width, 75); - XCTAssertEqual(overlay.frame.size.height, 75); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); -} - -// A, B, and C are the layers above the platform view. -// +-------------------------+ -// | PV +-----------+ | -// | +---+ | B +-----+ | | -// | | C | +---| A |-+ | -// | +---+ +-----+ | -// +-------------------------+ -- (void)testOneOverlayAndTwoIntersectingOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-one-overlay-two-intersecting-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay1.exists); - XCTAssertEqual(overlay1.frame.origin.x, 150); - XCTAssertEqual(overlay1.frame.origin.y, 150); - XCTAssertEqual(overlay1.frame.size.width, 75); - XCTAssertEqual(overlay1.frame.size.height, 75); - - XCUIElement* overlay2 = app.otherElements[@"platform_view[0].overlay[1]"]; - XCTAssertTrue(overlay2.exists); - XCTAssertEqual(overlay2.frame.origin.x, 75); - XCTAssertEqual(overlay2.frame.origin.y, 225); - XCTAssertEqual(overlay2.frame.size.width, 50); - XCTAssertEqual(overlay2.frame.size.height, 50); -} - -// A is the layer, which z index is higher than the platform view. -// +--------+ -// | PV | +---+ -// +--------+ | A | -// +--------+ +---+ -// | PV | -// +--------+ -- (void)testMultiplePlatformViewsWithoutOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-multiple-without-overlays" ]; - [app launch]; - - XCUIElement* platform_view1 = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view1.exists); - XCTAssertEqual(platform_view1.frame.origin.x, 25); - XCTAssertEqual(platform_view1.frame.origin.y, 325); - XCTAssertEqual(platform_view1.frame.size.width, 250); - XCTAssertEqual(platform_view1.frame.size.height, 250); - - XCUIElement* platform_view2 = app.textViews[@"platform_view[1]"]; - XCTAssertTrue(platform_view2.exists); - XCTAssertEqual(platform_view2.frame.origin.x, 25); - XCTAssertEqual(platform_view2.frame.origin.y, 25); - XCTAssertEqual(platform_view2.frame.size.width, 250); - XCTAssertEqual(platform_view2.frame.size.height, 250); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[0]"].exists); - XCTAssertFalse(app.otherElements[@"platform_view[1].overlay[0]"].exists); -} - -// A is the layer above both platform view. -// +------------+ -// | PV +----+ | -// +-----| A |-+ -// +-----| |-+ -// | PV +----+ | -// +------------+ -- (void)testMultiplePlatformViewsWithOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-multiple-background-foreground" ]; - [app launch]; - - XCUIElement* platform_view1 = app.textViews[@"platform_view[8]"]; - XCTAssertTrue(platform_view1.exists); - XCTAssertEqual(platform_view1.frame.origin.x, 25); - XCTAssertEqual(platform_view1.frame.origin.y, 325); - XCTAssertEqual(platform_view1.frame.size.width, 250); - XCTAssertEqual(platform_view1.frame.size.height, 250); - - XCUIElement* platform_view2 = app.textViews[@"platform_view[9]"]; - XCTAssertTrue(platform_view2.exists); - XCTAssertEqual(platform_view2.frame.origin.x, 25); - XCTAssertEqual(platform_view2.frame.origin.y, 25); - XCTAssertEqual(platform_view2.frame.size.width, 250); - XCTAssertEqual(platform_view2.frame.size.height, 250); - - XCUIElement* overlay1 = app.otherElements[@"platform_view[8].overlay[0]"]; - XCTAssertTrue(overlay1.exists); - XCTAssertEqual(overlay1.frame.origin.x, 25); - XCTAssertEqual(overlay1.frame.origin.y, 325); - XCTAssertEqual(overlay1.frame.size.width, 225); - XCTAssertEqual(overlay1.frame.size.height, 175); - - XCUIElement* overlay2 = app.otherElements[@"platform_view[9].overlay[0]"]; - XCTAssertTrue(overlay2.exists); - XCTAssertEqual(overlay2.frame.origin.x, 25); - XCTAssertEqual(overlay2.frame.origin.y, 25); - XCTAssertEqual(overlay2.frame.size.width, 225); - XCTAssertEqual(overlay2.frame.size.height, 250); -} - -// More then two overlays are merged into a single layer. -// +---------------------+ -// | +---+ +---+ +---+ | -// | | A | | B | | C | | -// | +---+ +---+ +---+ | -// | +-------+ | -// +-| D |-----------+ -// +-------+ -- (void)testPlatformViewsMaxOverlays { - XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--platform-view-max-overlays" ]; - [app launch]; - - XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; - XCTAssertTrue(platform_view.exists); - XCTAssertEqual(platform_view.frame.origin.x, 25); - XCTAssertEqual(platform_view.frame.origin.y, 25); - XCTAssertEqual(platform_view.frame.size.width, 250); - XCTAssertEqual(platform_view.frame.size.height, 250); - - XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; - XCTAssertTrue(overlay.exists); - XCTAssertEqual(overlay.frame.origin.x, 75); - XCTAssertEqual(overlay.frame.origin.y, 85); - XCTAssertEqual(overlay.frame.size.width, 150); - XCTAssertEqual(overlay.frame.size.height, 190); - - XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); -} - -@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png index 9ec19ab474f03b6cc7786cb038ee3b3e6d0c51a4..a193faeb040223e56343903c777f9181edb5bcc0 100644 GIT binary patch literal 30022 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ΠAr29j`n?8;-QZE z4jSFDu$|OBK+1@&kY%gDyh4f+et*UW!xwP*gQNm<3K_Yi(Xv2`G0u{G@CDL_`Za|R z0&ba{A^qz!h73(?gBQ zaOaF%@BB*^QHtUx^a&4#swelbeQ@;feaTS45;*zevVw|@B_*Du_fg$vTTApLhT z;(~N8NauodE=cEg)wx}DZdaZ22H-9H%#@2te_y_G)xmx1@#nP+_vs7wCD1Qx;69A{ z-@X&;4$L>JfxjakBUrV-?pPC7bYAj`F8b4z{e%L^L5)Ap&Ej}pv9OANp0FJk6lCu2 z`qwMCQ-p#C$T2zwwupZl2?j`8#a1jqb& z0=E@=#V?uu=M`PWHYtj}0{Y_#&Wfu+&iGHHKM&yc@?V(~vi|c9FG*CCezoXdPx+n% zIcrwd|B-1q@2k?|%s;R2x|fU6rSJXgF(6k7@Me>%1g;WLALUvCG}*aI;3|QWvbdJO zwFIsuaDJV@wFIsua4msbJ8%mq_&GOM30x(hZ4Is^K;6hy0#^x~-^Fq*;eTWaOX`;t XeZBM9{0{PeD?M`X*ny1wR>A)dG2_6W literal 20295 zcmeHuX*iVa8@HyJnz3Yzr3E$ieMzzvCQH_kA+oDbk8B}3O<5*oDSL~pkUjg(q>u%s=030UI)CT+JD2M|+}G1lqoY1XO+`gTr=hN* zPelcrrJ_PeAz|PX)smMN!DXMTzM2wMUK`sa_>ZTJv4-u13si#O9!Z7RhoRaJJp%qx z?K?(=pxjeYY3^hF_g;S=|DR{Tnjtr+;D4Sm0axfxB=`Z+f3C1(*xx5$v&sAa-Xo-- zN15f^xWEPFtbW;*ii(8?`q`(U&pi)5WV@lFYXGj$#-Yno@DKm)6}p!srTj#Pg6l&K zm2(E3`=@BVmW3Qoo zEAE=V()MiS$ENStBiA{d&TI4$Uu9`u;g8Qjx^L@!$Qxhsl75%>84gx2Zkt9p zRMxIl)orxYi*C=;2XrpKI37oy58lCFSM-=&@xF=oTnrW3#;5$UiQAH?R`gyXGY?cu zrq>A6T!x{2ge|-jD(&p3OzZK`!Cx68%Q=PKgb{}sqw2|M$ zql9LA7VneSbIERwo4HO~Pv=4E+IARkA+J5RTrU5ixIMDd-Gj|3_n70e{kVRPqm%YbIrEf=)%WS0xw@4K>=zfOA>tKgx*wpiLK}#s8`F)Z8tF7a!t7z7<4>CBITKz z!{_m5FWzUiD$~o$dd}A|mHK|YyyH4I(Bw_OI$M<_R7_9vIen|xbfYUnVx`aZZ%(5}4Y;&(4*>&L_jytIj{Ezty%+*$Wvj~__PCB!-R41mQZ@4VB z(*JF1Rc{KP5FV~<_snHf?8gPCb<3b_3%5`07E%0D?E@JS){IU*z3tSj^fc0a9lab$ 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 diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png index a2e9351d385e0b2f7800ec71448a05e2736e196e..793082f8f0f7c4edaa29fd14c5ee5a782d1e69b1 100644 GIT binary patch literal 33727 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%0D$f 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 HeDr?+Nh376 literal 22360 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 mf=d=$vf%&Cf@1cqGyR|38zWqd;cH7-*4ss diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 0ab3f7c5352f1..494d6585aa7fe 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -19,12 +19,6 @@ import 'src/touches_scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), - 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1), 'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2), 'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3), diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index fcea41a65f7d5..0dde98fd9eebb 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -48,224 +48,6 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin } } -/// A simple platform view with overlay that doesn't intersect with the platform view. -class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewNoOverlayIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - finishBuilderByAddingPlatformViewAndPicture( - builder, - 0, - overlayOffset: const Offset(150, 350), - ); - } -} - -/// A simple platform view with an overlay that partially intersects with the platform view. -class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewPartialIntersectionScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - finishBuilderByAddingPlatformViewAndPicture( - builder, - 0, - overlayOffset: const Offset(150, 250), - ); - } -} - -/// A simple platform view with two overlays that intersect with each other and the platform view. -class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// A simple platform view with one overlay and two overlays that intersect with each other and the platform view. -class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewOneOverlayTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// Two platform views without an overlay intersecting either platform view. -class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - MultiPlatformViewWithoutOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - builder.pushOffset(0, 600); - _addPlatformViewtoScene(builder, 0, 500, 500); - builder.pop(); - - _addPlatformViewtoScene(builder, 1, 500, 500); - - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTRB(0, 0, 100, 1000), - Paint()..color = const Color(0xFFFF0000), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(580, 0), picture); - - builder.pop(); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - -/// A simple platform view with too many overlays result in a single native view. -class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformView scenario. - /// - /// The [window] parameter must not be null. - PlatformViewMaxOverlaysScenario(Window window, String text, {int id = 0}) - : assert(window != null), - super(window) { - createPlatformView(window, text, id); - } - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder(); - - builder.pushOffset(0, 0); - - _addPlatformViewtoScene(builder, 0, 500, 500); - final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, -80), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - final Picture picture = recorder.endRecording(); - builder.addPicture(const Offset(300, 300), picture); - final Scene scene = builder.build(); - window.render(scene); - scene.dispose(); - } -} - /// Builds a scene with 2 platform views. class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. @@ -642,17 +424,12 @@ mixin _BasePlatformViewScenarioMixin on Scenario { } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture( - SceneBuilder sceneBuilder, - int viewId, { - Offset overlayOffset, - }) { - overlayOffset ??= const Offset(50, 50); + void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( - overlayOffset, + const Offset(50, 50), 50, Paint()..color = const Color(0xFFABCDEF), ); From 97083b48f39f2f75164c5a4b50ef60c6c0fcbf7c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 19:16:02 -0400 Subject: [PATCH 142/386] Roll src/third_party/dart 232308723704..cd8f712bcb01 (18 commits) (#17328) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6004eb1388d8d..b30d0fa5f21d0 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '232308723704677c2a3daa6e2f4bd77a5a7b6510', + 'dart_revision': 'cd8f712bcb01cf5c6b6b29a3dfeaed63ef3f6f78', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 95d8981684ce6..14b66861c452d 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: c15991dd303125cadfea57eb6d3c3bcf +Signature: 3382747e134612eb0bb34456be40f5c9 UNUSED LICENSES: From 23dff31a38345967aed194165a6a64c8eb7cef93 Mon Sep 17 00:00:00 2001 From: Filip Filmar Date: Wed, 25 Mar 2020 16:31:46 -0700 Subject: [PATCH 143/386] [runner] Corrects logging of close() status (#16698) close() returns a nonzero in case of an error. Old code had it log only when *no* error happens on close, which is exactly the opposite of what we want. --- shell/platform/fuchsia/flutter/runner.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/fuchsia/flutter/runner.cc b/shell/platform/fuchsia/flutter/runner.cc index 6459236730e16..8933f963df36b 100644 --- a/shell/platform/fuchsia/flutter/runner.cc +++ b/shell/platform/fuchsia/flutter/runner.cc @@ -75,7 +75,7 @@ bool InitializeTZData() { << strerror(errno); return false; } - if (!close(fd)) { + if (close(fd)) { FML_LOG(WARNING) << "Could not close: " << tzdata_dir << ": " << strerror(errno); } From 5845d70b50a74d2b0f83a5d130d7f79f0feb22c0 Mon Sep 17 00:00:00 2001 From: Yegor Date: Wed, 25 Mar 2020 17:25:06 -0700 Subject: [PATCH 144/386] Preserve zones in Window callbacks (#17298) preserve Window callback zones --- lib/ui/hooks.dart | 19 +- lib/ui/window.dart | 4 +- lib/web_ui/lib/src/engine.dart | 10 +- lib/web_ui/lib/src/engine/dom_renderer.dart | 4 +- lib/web_ui/lib/src/engine/history.dart | 8 +- lib/web_ui/lib/src/engine/keyboard.dart | 4 +- .../lib/src/engine/pointer_binding.dart | 5 +- .../src/engine/semantics/incrementable.dart | 4 +- .../lib/src/engine/semantics/scrollable.dart | 8 +- .../lib/src/engine/semantics/semantics.dart | 4 +- .../lib/src/engine/semantics/tappable.dart | 2 +- .../lib/src/engine/semantics/text_field.dart | 6 +- .../src/engine/text_editing/text_editing.dart | 12 +- lib/web_ui/lib/src/engine/util.dart | 11 + lib/web_ui/lib/src/engine/window.dart | 277 +++++++++++++++++- lib/web_ui/lib/src/ui/text.dart | 15 +- lib/web_ui/lib/src/ui/window.dart | 86 ++---- lib/web_ui/test/engine/window_test.dart | 225 ++++++++++++++ 18 files changed, 575 insertions(+), 129 deletions(-) create mode 100644 lib/web_ui/test/engine/window_test.dart diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index d3f68a9b18537..e7c70e51f9a06 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -146,7 +146,7 @@ void _updateAccessibilityFeatures(int values) { if (newFeatures == window._accessibilityFeatures) return; window._accessibilityFeatures = newFeatures; - _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFlagsChangedZone); + _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFeaturesChangedZone); } @pragma('vm:entry-point') @@ -276,22 +276,7 @@ void _invoke1(void callback(A a), Zone zone, A arg) { } } -/// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. -// ignore: unused_element -void _invoke2(void callback(A1 a1, A2 a2), Zone zone, A1 arg1, A2 arg2) { - if (callback == null) - return; - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(arg1, arg2); - } else { - zone.runBinaryGuarded(callback, arg1, arg2); - } -} - -/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2] and [arg3]. +/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { if (callback == null) return; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index d52bfea1a6c3c..4c4b9fe9d94ee 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -1098,10 +1098,10 @@ class Window { /// callback was set. VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; VoidCallback _onAccessibilityFeaturesChanged; - Zone _onAccessibilityFlagsChangedZone; + Zone _onAccessibilityFeaturesChangedZone; set onAccessibilityFeaturesChanged(VoidCallback callback) { _onAccessibilityFeaturesChanged = callback; - _onAccessibilityFlagsChangedZone = Zone.current; + _onAccessibilityFeaturesChangedZone = Zone.current; } /// Change the retained semantics data about this window. diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 04e688ff090e7..f735c78ea5612 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -181,17 +181,17 @@ void webOnlyInitializeEngine() { // microsecond precision, and only then convert to `int`. final int highResTimeMicroseconds = (1000 * highResTime).toInt(); - if (ui.window.onBeginFrame != null) { - ui.window - .onBeginFrame(Duration(microseconds: highResTimeMicroseconds)); + if (window._onBeginFrame != null) { + window + .invokeOnBeginFrame(Duration(microseconds: highResTimeMicroseconds)); } - if (ui.window.onDrawFrame != null) { + if (window._onDrawFrame != null) { // TODO(yjbanov): technically Flutter flushes microtasks between // onBeginFrame and onDrawFrame. We don't, which hasn't // been an issue yet, but eventually we'll have to // implement it properly. - ui.window.onDrawFrame(); + window.invokeOnDrawFrame(); } }); } diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index a5d630dd59de7..5990ad2f49d7a 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -467,8 +467,8 @@ flt-glass-pane * { /// Called immediately after browser window metrics change. void _metricsDidChange(html.Event event) { window._computePhysicalSize(); - if (ui.window.onMetricsChanged != null) { - ui.window.onMetricsChanged(); + if (window._onMetricsChanged != null) { + window.invokeOnMetricsChanged(); } } diff --git a/lib/web_ui/lib/src/engine/history.dart b/lib/web_ui/lib/src/engine/history.dart index 0c46004dc0117..ad7558f7ecfc5 100644 --- a/lib/web_ui/lib/src/engine/history.dart +++ b/lib/web_ui/lib/src/engine/history.dart @@ -94,8 +94,8 @@ class BrowserHistory { _setupFlutterEntry(_locationStrategy); // 2. Send a 'popRoute' platform message so the app can handle it accordingly. - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall(_popRouteMethodCall), (_) {}, @@ -113,8 +113,8 @@ class BrowserHistory { _userProvidedRouteName = null; // Send a 'pushRoute' platform message so the app handles it accordingly. - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall( MethodCall('pushRoute', newRouteName), diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index d42b44fc6f829..4183cd3054256 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -68,7 +68,7 @@ class Keyboard { static const JSONMessageCodec _messageCodec = JSONMessageCodec(); void _handleHtmlEvent(html.KeyboardEvent event) { - if (ui.window.onPlatformMessage == null) { + if (window._onPlatformMessage == null) { return; } @@ -88,7 +88,7 @@ class Keyboard { 'metaState': _getMetaState(event), }; - ui.window.onPlatformMessage('flutter/keyevent', + window.invokeOnPlatformMessage('flutter/keyevent', _messageCodec.encodeMessage(eventData), _noopCallback); } diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index 6b6726f3b6ce7..d88e1d4074d84 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -125,9 +125,8 @@ class PointerBinding { void _onPointerData(Iterable data) { final ui.PointerDataPacket packet = ui.PointerDataPacket(data: data.toList()); - final ui.PointerDataPacketCallback callback = ui.window.onPointerDataPacket; - if (callback != null) { - callback(packet); + if (window._onPointerDataPacket != null) { + window.invokeOnPointerDataPacket(packet); } } } diff --git a/lib/web_ui/lib/src/engine/semantics/incrementable.dart b/lib/web_ui/lib/src/engine/semantics/incrementable.dart index e61baf76188d5..5ae975aa4fd1f 100644 --- a/lib/web_ui/lib/src/engine/semantics/incrementable.dart +++ b/lib/web_ui/lib/src/engine/semantics/incrementable.dart @@ -53,11 +53,11 @@ class Incrementable extends RoleManager { final int newInputValue = int.parse(_element.value); if (newInputValue > _currentSurrogateValue) { _currentSurrogateValue += 1; - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.increase, null); } else if (newInputValue < _currentSurrogateValue) { _currentSurrogateValue -= 1; - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.decrease, null); } }); diff --git a/lib/web_ui/lib/src/engine/semantics/scrollable.dart b/lib/web_ui/lib/src/engine/semantics/scrollable.dart index 0f847d631098e..0c818314a26f1 100644 --- a/lib/web_ui/lib/src/engine/semantics/scrollable.dart +++ b/lib/web_ui/lib/src/engine/semantics/scrollable.dart @@ -53,20 +53,20 @@ class Scrollable extends RoleManager { final int semanticsId = semanticsObject.id; if (doScrollForward) { if (semanticsObject.isVerticalScrollContainer) { - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollUp, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollLeft, null); } } else { if (semanticsObject.isVerticalScrollContainer) { - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollDown, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollRight, null); } } diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 0554a1a48350a..a37fb7e19bae9 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -1236,8 +1236,8 @@ class EngineSemanticsOwner { _gestureModeClock?.datetime = null; } - if (ui.window.onSemanticsEnabledChanged != null) { - ui.window.onSemanticsEnabledChanged(); + if (window._onSemanticsEnabledChanged != null) { + window.invokeOnSemanticsEnabledChanged(); } } diff --git a/lib/web_ui/lib/src/engine/semantics/tappable.dart b/lib/web_ui/lib/src/engine/semantics/tappable.dart index 25b94819d4882..e60c9881918e5 100644 --- a/lib/web_ui/lib/src/engine/semantics/tappable.dart +++ b/lib/web_ui/lib/src/engine/semantics/tappable.dart @@ -39,7 +39,7 @@ class Tappable extends RoleManager { GestureMode.browserGestures) { return; } - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.tap, null); }; element.addEventListener('click', _clickListener); diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 4091ece5854db..5018d8d2d5aa2 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -149,8 +149,8 @@ class TextField extends RoleManager { } textEditing.useCustomEditableElement(textEditingElement); - ui.window - .onSemanticsAction(semanticsObject.id, ui.SemanticsAction.tap, null); + window + .invokeOnSemanticsAction(semanticsObject.id, ui.SemanticsAction.tap, null); }); } @@ -187,7 +187,7 @@ class TextField extends RoleManager { if (offsetX * offsetX + offsetY * offsetY < kTouchSlop) { // Recognize it as a tap that requires a keyboard. - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.tap, null); } } else { diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index b13b0a168e604..040e11f3036b3 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -823,8 +823,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.updateEditingState' message to the framework. void updateEditingState(int clientId, EditingState editingState) { - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall('TextInputClient.updateEditingState', [ @@ -839,8 +839,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.performAction' message to the framework. void performAction(int clientId, String inputAction) { - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall( @@ -855,8 +855,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.onConnectionClosed' message to the framework. void onConnectionClosed(int clientId) { - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall( diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 54a03397de936..4ca7c5c540eb9 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -444,3 +444,14 @@ void applyWebkitClipFix(html.Element containerElement) { containerElement.style.zIndex = '0'; } } + +final ByteData _fontChangeMessage = JSONMessageCodec().encodeMessage({'type': 'fontsChange'}); + +FutureOr sendFontChangeMessage() async { + if (window._onPlatformMessage != null) + window.invokeOnPlatformMessage( + 'flutter/system', + _fontChangeMessage, + (_) {}, + ); +} diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 8bfb18039b5fb..03695abc096b3 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -110,19 +110,248 @@ class EngineWindow extends ui.Window { _browserHistory.locationStrategy = strategy; } + @override + ui.VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; + ui.VoidCallback _onTextScaleFactorChanged; + Zone _onTextScaleFactorChangedZone; + @override + set onTextScaleFactorChanged(ui.VoidCallback callback) { + _onTextScaleFactorChanged = callback; + _onTextScaleFactorChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnTextScaleFactorChanged() { + _invoke(_onTextScaleFactorChanged, _onTextScaleFactorChangedZone); + } + + @override + ui.VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + ui.VoidCallback _onPlatformBrightnessChanged; + Zone _onPlatformBrightnessChangedZone; + @override + set onPlatformBrightnessChanged(ui.VoidCallback callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformBrightnessChanged() { + _invoke(_onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); + } + + @override + ui.VoidCallback get onMetricsChanged => _onMetricsChanged; + ui.VoidCallback _onMetricsChanged; + Zone _onMetricsChangedZone; + @override + set onMetricsChanged(ui.VoidCallback callback) { + _onMetricsChanged = callback; + _onMetricsChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnMetricsChanged() { + _invoke(_onMetricsChanged, _onMetricsChangedZone); + } + + @override + ui.VoidCallback get onLocaleChanged => _onLocaleChanged; + ui.VoidCallback _onLocaleChanged; + Zone _onLocaleChangedZone; + @override + set onLocaleChanged(ui.VoidCallback callback) { + _onLocaleChanged = callback; + _onLocaleChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnLocaleChanged() { + _invoke(_onLocaleChanged, _onLocaleChangedZone); + } + + @override + ui.FrameCallback get onBeginFrame => _onBeginFrame; + ui.FrameCallback _onBeginFrame; + Zone _onBeginFrameZone; + @override + set onBeginFrame(ui.FrameCallback callback) { + _onBeginFrame = callback; + _onBeginFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnBeginFrame(Duration duration) { + _invoke1(_onBeginFrame, _onBeginFrameZone, duration); + } + + @override + ui.TimingsCallback get onReportTimings => _onReportTimings; + ui.TimingsCallback _onReportTimings; + Zone _onReportTimingsZone; + @override + set onReportTimings(ui.TimingsCallback callback) { + _onReportTimings = callback; + _onReportTimingsZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnReportTimings(List timings) { + _invoke1>(_onReportTimings, _onReportTimingsZone, timings); + } + + @override + ui.VoidCallback get onDrawFrame => _onDrawFrame; + ui.VoidCallback _onDrawFrame; + Zone _onDrawFrameZone; + @override + set onDrawFrame(ui.VoidCallback callback) { + _onDrawFrame = callback; + _onDrawFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnDrawFrame() { + _invoke(_onDrawFrame, _onDrawFrameZone); + } + + @override + ui.PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; + ui.PointerDataPacketCallback _onPointerDataPacket; + Zone _onPointerDataPacketZone; + @override + set onPointerDataPacket(ui.PointerDataPacketCallback callback) { + _onPointerDataPacket = callback; + _onPointerDataPacketZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPointerDataPacket(ui.PointerDataPacket packet) { + _invoke1(_onPointerDataPacket, _onPointerDataPacketZone, packet); + } + + @override + ui.VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + ui.VoidCallback _onSemanticsEnabledChanged; + Zone _onSemanticsEnabledChangedZone; + @override + set onSemanticsEnabledChanged(ui.VoidCallback callback) { + _onSemanticsEnabledChanged = callback; + _onSemanticsEnabledChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsEnabledChanged() { + _invoke(_onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); + } + + @override + ui.SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; + ui.SemanticsActionCallback _onSemanticsAction; + Zone _onSemanticsActionZone; + @override + set onSemanticsAction(ui.SemanticsActionCallback callback) { + _onSemanticsAction = callback; + _onSemanticsActionZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsAction(int id, ui.SemanticsAction action, ByteData args) { + _invoke3(_onSemanticsAction, + _onSemanticsActionZone, id, action, args); + } + + @override + ui.VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + ui.VoidCallback _onAccessibilityFeaturesChanged; + Zone _onAccessibilityFeaturesChangedZone; + @override + set onAccessibilityFeaturesChanged(ui.VoidCallback callback) { + _onAccessibilityFeaturesChanged = callback; + _onAccessibilityFeaturesChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnAccessibilityFeaturesChanged() { + _invoke(_onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); + } + + @override + ui.PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; + ui.PlatformMessageCallback _onPlatformMessage; + Zone _onPlatformMessageZone; + @override + set onPlatformMessage(ui.PlatformMessageCallback callback) { + _onPlatformMessage = callback; + _onPlatformMessageZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformMessage(String name, ByteData data, ui.PlatformMessageResponseCallback callback) { + _invoke3( + _onPlatformMessage, + _onPlatformMessageZone, + name, + data, + callback, + ); + } + @override void sendPlatformMessage( String name, ByteData data, ui.PlatformMessageResponseCallback callback, + ) { + _sendPlatformMessage(name, data, _zonedPlatformMessageResponseCallback(callback)); + } + + /// Wraps the given [callback] in another callback that ensures that the + /// original callback is called in the zone it was registered in. + static ui.PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback callback) { + if (callback == null) + return null; + + // Store the zone in which the callback is being registered. + final Zone registrationZone = Zone.current; + + return (ByteData data) { + registrationZone.runUnaryGuarded(callback, data); + }; + } + + void _sendPlatformMessage( + String name, + ByteData data, + ui.PlatformMessageResponseCallback callback, ) { // In widget tests we want to bypass processing of platform messages. if (assertionsEnabled && ui.debugEmulateFlutterTesterEnvironment) { return; } + if (_debugPrintPlatformMessages) { print('Sent platform message on channel: "$name"'); } + + if (assertionsEnabled && name == 'flutter/debug-echo') { + // Echoes back the data unchanged. Used for testing purpopses. + _replyToPlatformMessage(callback, data); + return; + } + switch (name) { case 'flutter/assets': assert(ui.webOnlyAssetManager != null); @@ -272,7 +501,9 @@ class EngineWindow extends ui.Window { _platformBrightness = newPlatformBrightness; if (previousPlatformBrightness != _platformBrightness && - onPlatformBrightnessChanged != null) onPlatformBrightnessChanged(); + onPlatformBrightnessChanged != null) { + invokeOnPlatformBrightnessChanged(); + } } /// Reference to css media query that indicates the user theme preference on the web. @@ -336,6 +567,50 @@ bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { return false; } +/// Invokes [callback] inside the given [zone]. +void _invoke(void callback(), Zone zone) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(); + } else { + zone.runGuarded(callback); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg]. +void _invoke1(void callback(A a), Zone zone, A arg) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg); + } else { + zone.runUnaryGuarded(callback, arg); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. +void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg1, arg2, arg3); + } else { + zone.runGuarded(() { + callback(arg1, arg2, arg3); + }); + } +} + /// The window singleton. /// /// `dart:ui` window delegates to this value. However, this value has a wider diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index aeba957a8da7b..8a1ae89fab5a6 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1590,22 +1590,11 @@ abstract class ParagraphBuilder { Future loadFontFromList(Uint8List list, {String fontFamily}) { if (engine.experimentalUseSkia) { return engine.skiaFontCollection.loadFontFromList(list, fontFamily: fontFamily).then( - (_) => _sendFontChangeMessage() + (_) => engine.sendFontChangeMessage() ); } else { return _fontCollection.loadFontFromList(list, fontFamily: fontFamily).then( - (_) => _sendFontChangeMessage() + (_) => engine.sendFontChangeMessage() ); } } - -final ByteData _fontChangeMessage = engine.JSONMessageCodec().encodeMessage({'type': 'fontsChange'}); - -FutureOr _sendFontChangeMessage() async { - if (window.onPlatformMessage != null) - window.onPlatformMessage( - 'flutter/system', - _fontChangeMessage, - (_) {}, - ); -} diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index f19432c4e0832..0586af5dd1891 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -621,11 +621,8 @@ abstract class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; - VoidCallback _onTextScaleFactorChanged; - set onTextScaleFactorChanged(VoidCallback callback) { - _onTextScaleFactorChanged = callback; - } + VoidCallback get onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback callback); /// The setting indicating the current brightness mode of the host platform. Brightness get platformBrightness; @@ -639,11 +636,8 @@ abstract class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; - VoidCallback _onPlatformBrightnessChanged; - set onPlatformBrightnessChanged(VoidCallback callback) { - _onPlatformBrightnessChanged = callback; - } + VoidCallback get onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback callback); /// A callback that is invoked whenever the [devicePixelRatio], /// [physicalSize], [padding], or [viewInsets] values change, for example @@ -661,11 +655,8 @@ abstract class Window { /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// register for notifications when this is called. /// * [MediaQuery.of], a simpler mechanism for the same. - VoidCallback get onMetricsChanged => _onMetricsChanged; - VoidCallback _onMetricsChanged; - set onMetricsChanged(VoidCallback callback) { - _onMetricsChanged = callback; - } + VoidCallback get onMetricsChanged; + set onMetricsChanged(VoidCallback callback); static const _enUS = const Locale('en', 'US'); @@ -713,11 +704,8 @@ abstract class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onLocaleChanged => _onLocaleChanged; - VoidCallback _onLocaleChanged; - set onLocaleChanged(VoidCallback callback) { - _onLocaleChanged = callback; - } + VoidCallback get onLocaleChanged; + set onLocaleChanged(VoidCallback callback); /// Requests that, at the next appropriate opportunity, the [onBeginFrame] /// and [onDrawFrame] callbacks be invoked. @@ -753,11 +741,8 @@ abstract class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - FrameCallback get onBeginFrame => _onBeginFrame; - FrameCallback _onBeginFrame; - set onBeginFrame(FrameCallback callback) { - _onBeginFrame = callback; - } + FrameCallback get onBeginFrame; + set onBeginFrame(FrameCallback callback); /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. @@ -775,12 +760,8 @@ abstract class Window { /// decrease the overhead (as this is available in the release mode). The /// timing of any frame will be sent within about 1 second even if there are /// no later frames to batch. - TimingsCallback get onReportTimings => _onReportTimings; - TimingsCallback _onReportTimings; - Zone _onReportTimingsZone; // ignore: unused_field - set onReportTimings(TimingsCallback callback) { - _onReportTimings = callback; - } + TimingsCallback get onReportTimings; + set onReportTimings(TimingsCallback callback); /// A callback that is invoked for each frame after [onBeginFrame] has /// completed and after the microtask queue has been drained. This can be @@ -796,11 +777,8 @@ abstract class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - VoidCallback get onDrawFrame => _onDrawFrame; - VoidCallback _onDrawFrame; - set onDrawFrame(VoidCallback callback) { - _onDrawFrame = callback; - } + VoidCallback get onDrawFrame; + set onDrawFrame(VoidCallback callback); /// A callback that is invoked when pointer data is available. /// @@ -811,11 +789,8 @@ abstract class Window { /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. - PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; - PointerDataPacketCallback _onPointerDataPacket; - set onPointerDataPacket(PointerDataPacketCallback callback) { - _onPointerDataPacket = callback; - } + PointerDataPacketCallback get onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback callback); /// The route or path that the embedder requested when the application was /// launched. @@ -864,11 +839,8 @@ abstract class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - VoidCallback _onSemanticsEnabledChanged; - set onSemanticsEnabledChanged(VoidCallback callback) { - _onSemanticsEnabledChanged = callback; - } + VoidCallback get onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback callback); /// A callback that is invoked whenever the user requests an action to be /// performed. @@ -878,22 +850,15 @@ abstract class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; - SemanticsActionCallback _onSemanticsAction; - set onSemanticsAction(SemanticsActionCallback callback) { - _onSemanticsAction = callback; - } + SemanticsActionCallback get onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback callback); /// A callback that is invoked when the value of [accessibilityFlags] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback get onAccessibilityFeaturesChanged => - _onAccessibilityFeaturesChanged; - VoidCallback _onAccessibilityFeaturesChanged; - set onAccessibilityFeaturesChanged(VoidCallback callback) { - _onAccessibilityFeaturesChanged = callback; - } + VoidCallback get onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback callback); /// Called whenever this window receives a message from a platform-specific /// plugin. @@ -908,11 +873,8 @@ abstract class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; - PlatformMessageCallback _onPlatformMessage; - set onPlatformMessage(PlatformMessageCallback callback) { - _onPlatformMessage = callback; - } + PlatformMessageCallback get onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback callback); /// Change the retained semantics data about this window. /// diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart new file mode 100644 index 0000000000000..4dd297627285e --- /dev/null +++ b/lib/web_ui/test/engine/window_test.dart @@ -0,0 +1,225 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:test/test.dart'; +import 'package:ui/ui.dart' as ui; +import 'package:ui/src/engine.dart'; + +void main() { + test('onTextScaleFactorChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onTextScaleFactorChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onTextScaleFactorChanged, same(callback)); + }); + + window.invokeOnTextScaleFactorChanged(); + }); + + test('onPlatformBrightnessChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onPlatformBrightnessChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onPlatformBrightnessChanged, same(callback)); + }); + + window.invokeOnPlatformBrightnessChanged(); + }); + + test('onMetricsChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onMetricsChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onMetricsChanged, same(callback)); + }); + + window.invokeOnMetricsChanged(); + }); + + test('onLocaleChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onLocaleChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onLocaleChanged, same(callback)); + }); + + window.invokeOnLocaleChanged(); + }); + + test('onBeginFrame preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.FrameCallback callback = (_) { + expect(Zone.current, innerZone); + }; + window.onBeginFrame = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onBeginFrame, same(callback)); + }); + + window.invokeOnBeginFrame(null); + }); + + test('onReportTimings preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.TimingsCallback callback = (_) { + expect(Zone.current, innerZone); + }; + window.onReportTimings = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onReportTimings, same(callback)); + }); + + window.invokeOnReportTimings(null); + }); + + test('onDrawFrame preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onDrawFrame = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onDrawFrame, same(callback)); + }); + + window.invokeOnDrawFrame(); + }); + + test('onPointerDataPacket preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.PointerDataPacketCallback callback = (_) { + expect(Zone.current, innerZone); + }; + window.onPointerDataPacket = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onPointerDataPacket, same(callback)); + }); + + window.invokeOnPointerDataPacket(null); + }); + + test('onSemanticsEnabledChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onSemanticsEnabledChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onSemanticsEnabledChanged, same(callback)); + }); + + window.invokeOnSemanticsEnabledChanged(); + }); + + test('onSemanticsAction preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.SemanticsActionCallback callback = (_, __, ___) { + expect(Zone.current, innerZone); + }; + window.onSemanticsAction = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onSemanticsAction, same(callback)); + }); + + window.invokeOnSemanticsAction(null, null, null); + }); + + test('onAccessibilityFeaturesChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onAccessibilityFeaturesChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onAccessibilityFeaturesChanged, same(callback)); + }); + + window.invokeOnAccessibilityFeaturesChanged(); + }); + + test('onPlatformMessage preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.PlatformMessageCallback callback = (_, __, ___) { + expect(Zone.current, innerZone); + }; + window.onPlatformMessage = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onPlatformMessage, same(callback)); + }); + + window.invokeOnPlatformMessage(null, null, null); + }); + + test('sendPlatformMessage preserves the zone', () async { + final Completer completer = Completer(); + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ByteData inputData = ByteData(4); + inputData.setUint32(0, 42); + window.sendPlatformMessage( + 'flutter/debug-echo', + inputData, + (outputData) { + expect(Zone.current, innerZone); + completer.complete(); + }, + ); + }); + + await completer.future; + }); +} From ba80c0c49ac8f4e8d35ac7e5d98489b1f0d0a2f5 Mon Sep 17 00:00:00 2001 From: renyou Date: Wed, 25 Mar 2020 18:02:46 -0700 Subject: [PATCH 145/386] Android change to match the Google internal changes. (#17331) * Android change to match the google internal changes. * Fix format. * Revert format change, add super call. * Remove comment for API version. --- .../android/io/flutter/app/FlutterFragmentActivity.java | 3 ++- .../io/flutter/embedding/android/FlutterFragmentActivity.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java index 2e881b6148673..3fc80cf703bb8 100644 --- a/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/app/FlutterFragmentActivity.java @@ -125,9 +125,10 @@ protected void onPostResume() { eventDelegate.onPostResume(); } - // @Override - added in API level 23 + @Override public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); } diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 9a7b6aa7d0925..4215d4d2c39c4 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -489,6 +489,7 @@ public void onBackPressed() { @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); flutterFragment.onRequestPermissionsResult(requestCode, permissions, grantResults); } From e4b2982724867de894dcee361c1828718f2b7587 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 25 Mar 2020 22:21:02 -0400 Subject: [PATCH 146/386] Roll fuchsia/sdk/core/mac-amd64 from o8vhc... to 6zho5... (#17334) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index b30d0fa5f21d0..5cdb3b1206ac2 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'o8vhcBMhLFl1PCgFR9WoyT4-Xv-wocqDzZt97bain4UC' + 'version': '6zho5oUADpegao7qM81YbY1frvdweFGrpMtA0qj2uhsC' } ], 'condition': 'host_os == "mac"', From 07d033026a1219ffebe89a306a655d78a69ab515 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 00:21:01 -0400 Subject: [PATCH 147/386] Roll src/third_party/dart cd8f712bcb01..bc53ecefae52 (4 commits) (#17338) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 5cdb3b1206ac2..6d83f9d531fd4 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'cd8f712bcb01cf5c6b6b29a3dfeaed63ef3f6f78', + 'dart_revision': 'bc53ecefae523208155cea798c13dd430bcc30e2', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -62,7 +62,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.16.1', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.113', + 'dart_linter_tag': '0.1.114', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.5', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 14b66861c452d..b0f854e1e2a54 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 3382747e134612eb0bb34456be40f5c9 +Signature: ec9289c8fd6a8d089a9d6c507cdcfc31 UNUSED LICENSES: From b3075b309170369f892f086733e67bbb54bd3416 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Wed, 25 Mar 2020 22:49:59 -0700 Subject: [PATCH 148/386] Rename GPU thread to raster thread in code comments (#17329) 1. Simple "GPU thread" to "raster thread" replacement. 2. Regex replace "GPU([\n\r\s]+//+ thread)" with "raster$1". 3. Regex replace "// gpu$" with "// raster". 4. Simple test change. 5. Run ci/format.sh --- flow/README.md | 2 +- flow/testing/mock_texture.h | 2 +- flow/texture.h | 24 +++++----- flow/view_holder.cc | 4 +- fml/gpu_thread_merger.h | 4 +- lib/ui/compositing.dart | 6 +-- lib/ui/compositing/scene_host.cc | 2 +- lib/ui/painting/image_decoder_unittests.cc | 16 +++---- lib/ui/painting/image_encoding.cc | 4 +- lib/ui/painting/multi_frame_codec.cc | 2 +- lib/ui/window.dart | 6 +-- .../lib/src/engine/surface/scene_builder.dart | 6 +-- lib/web_ui/lib/src/ui/compositing.dart | 6 +-- lib/web_ui/lib/src/ui/window.dart | 6 +-- shell/common/engine.h | 4 +- shell/common/platform_view.h | 13 ++--- shell/common/rasterizer.h | 4 +- shell/common/shell.cc | 47 ++++++++++--------- shell/common/shell.h | 10 ++-- shell/common/shell_test.cc | 2 +- shell/common/shell_unittests.cc | 6 +-- shell/gpu/gpu_surface_vulkan.cc | 6 +-- .../platform/android/android_shell_holder.cc | 6 +-- .../common/framework/Headers/FlutterTexture.h | 4 +- .../ios/framework/Source/FlutterEngine.mm | 4 +- .../framework/Source/FlutterViewController.mm | 5 +- shell/platform/darwin/ios/platform_view_ios.h | 2 +- .../platform/embedder/embedder_thread_host.cc | 4 +- .../embedder/tests/embedder_unittests.cc | 4 +- shell/platform/fuchsia/flutter/engine.cc | 4 +- .../fuchsia/flutter/session_connection.h | 2 +- shell/platform/fuchsia/flutter/surface.h | 4 +- .../fuchsia/flutter/vsync_waiter_unittests.cc | 2 +- shell/testing/tester_main.cc | 2 +- 34 files changed, 114 insertions(+), 111 deletions(-) diff --git a/flow/README.md b/flow/README.md index a0a37a03d5840..150281e2288e4 100644 --- a/flow/README.md +++ b/flow/README.md @@ -3,4 +3,4 @@ Flow Flow is a simple compositor based on Skia that the Flutter engine uses to cache recoded paint commands and pixels generated from those recordings. Flow runs on -the GPU thread and uploads information to the GPU. +the raster thread and uploads information to Skia. diff --git a/flow/testing/mock_texture.h b/flow/testing/mock_texture.h index c5339ebb77a66..5de0346f21629 100644 --- a/flow/testing/mock_texture.h +++ b/flow/testing/mock_texture.h @@ -25,7 +25,7 @@ class MockTexture : public Texture { explicit MockTexture(int64_t textureId); - // Called from GPU thread. + // Called from raster thread. void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, diff --git a/flow/texture.h b/flow/texture.h index 812588d382bb1..ad4734798716c 100644 --- a/flow/texture.h +++ b/flow/texture.h @@ -15,25 +15,25 @@ namespace flutter { class Texture { public: - Texture(int64_t id); // Called from UI or GPU thread. - virtual ~Texture(); // Called from GPU thread. + Texture(int64_t id); // Called from UI or raster thread. + virtual ~Texture(); // Called from raster thread. - // Called from GPU thread. + // Called from raster thread. virtual void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, GrContext* context) = 0; - // Called from GPU thread. + // Called from raster thread. virtual void OnGrContextCreated() = 0; - // Called from GPU thread. + // Called from raster thread. virtual void OnGrContextDestroyed() = 0; - // Called on GPU thread. + // Called on raster thread. virtual void MarkNewFrameAvailable() = 0; - // Called on GPU thread. + // Called on raster thread. virtual void OnTextureUnregistered() = 0; int64_t Id() { return id_; } @@ -48,19 +48,19 @@ class TextureRegistry { public: TextureRegistry(); - // Called from GPU thread. + // Called from raster thread. void RegisterTexture(std::shared_ptr texture); - // Called from GPU thread. + // Called from raster thread. void UnregisterTexture(int64_t id); - // Called from GPU thread. + // Called from raster thread. std::shared_ptr GetTexture(int64_t id); - // Called from GPU thread. + // Called from raster thread. void OnGrContextCreated(); - // Called from GPU thread. + // Called from raster thread. void OnGrContextDestroyed(); private: diff --git a/flow/view_holder.cc b/flow/view_holder.cc index 66ce3672a8431..499d7b9ca05bb 100644 --- a/flow/view_holder.cc +++ b/flow/view_holder.cc @@ -52,8 +52,8 @@ void ViewHolder::Create(zx_koid_t id, fml::RefPtr ui_task_runner, fuchsia::ui::views::ViewHolderToken view_holder_token, const BindCallback& on_bind_callback) { - // This GPU thread contains at least 1 ViewHolder. Initialize the per-thread - // bindings. + // This raster thread contains at least 1 ViewHolder. Initialize the + // per-thread bindings. if (tls_view_holder_bindings.get() == nullptr) { tls_view_holder_bindings.reset(new ViewHolderBindings()); } diff --git a/fml/gpu_thread_merger.h b/fml/gpu_thread_merger.h index 4b7cb5ae2e5df..8a28a43f71dc2 100644 --- a/fml/gpu_thread_merger.h +++ b/fml/gpu_thread_merger.h @@ -17,7 +17,7 @@ enum class GpuThreadStatus { kRemainsMerged, kRemainsUnmerged, kUnmergedNow }; class GpuThreadMerger : public fml::RefCountedThreadSafe { public: - // Merges the GPU thread into platform thread for the duration of + // Merges the raster thread into platform thread for the duration of // the lease term. Lease is managed by the caller by either calling // |ExtendLeaseTo| or |DecrementLease|. // When the caller merges with a lease term of say 2. The threads @@ -38,7 +38,7 @@ class GpuThreadMerger : public fml::RefCountedThreadSafe { // Returns true if the current thread owns rasterizing. // When the threads are merged, platform thread owns rasterizing. - // When un-merged, gpu thread owns rasterizing. + // When un-merged, raster thread owns rasterizing. bool IsOnRasterizingThread(); private: diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index 715ba20023278..d1f43ee983489 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -645,8 +645,8 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// controls where the statistics are displayed. /// /// enabledOptions is a bit field with the following bits defined: - /// - 0x01: displayRasterizerStatistics - show GPU thread frame time - /// - 0x02: visualizeRasterizerStatistics - graph GPU thread frame times + /// - 0x01: displayRasterizerStatistics - show raster thread frame time + /// - 0x02: visualizeRasterizerStatistics - graph raster thread frame times /// - 0x04: displayEngineStatistics - show UI thread frame time /// - 0x08: visualizeEngineStatistics - graph UI thread frame times /// Set enabledOptions to 0x0F to enable all the currently defined features. @@ -654,7 +654,7 @@ class SceneBuilder extends NativeFieldWrapperClass2 { /// The "UI thread" is the thread that includes all the execution of /// the main Dart isolate (the isolate that can call /// [Window.render]). The UI thread frame time is the total time - /// spent executing the [Window.onBeginFrame] callback. The "GPU + /// spent executing the [Window.onBeginFrame] callback. The "raster /// thread" is the thread (running on the CPU) that subsequently /// processes the [Scene] provided by the Dart code to turn it into /// GPU commands and send it to the GPU. diff --git a/lib/ui/compositing/scene_host.cc b/lib/ui/compositing/scene_host.cc index 7acf29ff6ec91..763b2bcb27e40 100644 --- a/lib/ui/compositing/scene_host.cc +++ b/lib/ui/compositing/scene_host.cc @@ -180,7 +180,7 @@ SceneHost::SceneHost(fml::RefPtr viewHolderToken, } // This callback will be posted as a task when the |scenic::ViewHolder| - // resource is created and given an id by the GPU thread. + // resource is created and given an id by the raster thread. auto bind_callback = [scene_host = this, isolate_service_id = isolate_service_id_](scenic::ResourceId id) { diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc index 94c008e2959d2..d90378dd625fe 100644 --- a/lib/ui/painting/image_decoder_unittests.cc +++ b/lib/ui/painting/image_decoder_unittests.cc @@ -125,7 +125,7 @@ TEST_F(ImageDecoderFixtureTest, CanCreateImageDecoder) { auto thread_task_runner = CreateNewThread(); TaskRunners runners(GetCurrentTestName(), // label thread_task_runner, // platform - thread_task_runner, // gpu + thread_task_runner, // raster thread_task_runner, // ui thread_task_runner // io @@ -146,7 +146,7 @@ TEST_F(ImageDecoderFixtureTest, InvalidImageResultsError) { auto thread_task_runner = CreateNewThread(); TaskRunners runners(GetCurrentTestName(), // label thread_task_runner, // platform - thread_task_runner, // gpu + thread_task_runner, // raster thread_task_runner, // ui thread_task_runner // io ); @@ -176,7 +176,7 @@ TEST_F(ImageDecoderFixtureTest, ValidImageResultsInSuccess) { auto loop = fml::ConcurrentMessageLoop::Create(); TaskRunners runners(GetCurrentTestName(), // label CreateNewThread("platform"), // platform - CreateNewThread("gpu"), // gpu + CreateNewThread("raster"), // raster CreateNewThread("ui"), // ui CreateNewThread("io") // io ); @@ -223,7 +223,7 @@ TEST_F(ImageDecoderFixtureTest, ExifDataIsRespectedOnDecode) { auto loop = fml::ConcurrentMessageLoop::Create(); TaskRunners runners(GetCurrentTestName(), // label CreateNewThread("platform"), // platform - CreateNewThread("gpu"), // gpu + CreateNewThread("raster"), // raster CreateNewThread("ui"), // ui CreateNewThread("io") // io ); @@ -275,7 +275,7 @@ TEST_F(ImageDecoderFixtureTest, CanDecodeWithoutAGPUContext) { auto loop = fml::ConcurrentMessageLoop::Create(); TaskRunners runners(GetCurrentTestName(), // label CreateNewThread("platform"), // platform - CreateNewThread("gpu"), // gpu + CreateNewThread("raster"), // raster CreateNewThread("ui"), // ui CreateNewThread("io") // io ); @@ -331,7 +331,7 @@ TEST_F(ImageDecoderFixtureTest, CanDecodeWithResizes) { auto loop = fml::ConcurrentMessageLoop::Create(); TaskRunners runners(GetCurrentTestName(), // label CreateNewThread("platform"), // platform - CreateNewThread("gpu"), // gpu + CreateNewThread("raster"), // raster CreateNewThread("ui"), // ui CreateNewThread("io") // io ); @@ -430,7 +430,7 @@ TEST_F(ImageDecoderFixtureTest, CanResizeWithoutDecode) { auto loop = fml::ConcurrentMessageLoop::Create(); TaskRunners runners(GetCurrentTestName(), // label CreateNewThread("platform"), // platform - CreateNewThread("gpu"), // gpu + CreateNewThread("raster"), // raster CreateNewThread("ui"), // ui CreateNewThread("io") // io ); @@ -590,7 +590,7 @@ TEST_F(ImageDecoderFixtureTest, TaskRunners runners(GetCurrentTestName(), // label CreateNewThread("platform"), // platform - CreateNewThread("gpu"), // gpu + CreateNewThread("raster"), // raster CreateNewThread("ui"), // ui CreateNewThread("io") // io ); diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc index cef92a7e2451a..6b56a74a36546 100644 --- a/lib/ui/painting/image_encoding.cc +++ b/lib/ui/painting/image_encoding.cc @@ -116,8 +116,8 @@ void ConvertImageToRaster(sk_sp image, } // Cross-context images do not support makeRasterImage. Convert these images - // by drawing them into a surface. This must be done on the GPU thread - // to prevent concurrent usage of the image on both the IO and GPU threads. + // by drawing them into a surface. This must be done on the raster thread + // to prevent concurrent usage of the image on both the IO and raster threads. gpu_task_runner->PostTask([image, encode_task = std::move(encode_task), resource_context, snapshot_delegate, io_task_runner]() { diff --git a/lib/ui/painting/multi_frame_codec.cc b/lib/ui/painting/multi_frame_codec.cc index ae2b4bdb83f02..6c3763b1ab529 100644 --- a/lib/ui/painting/multi_frame_codec.cc +++ b/lib/ui/painting/multi_frame_codec.cc @@ -124,7 +124,7 @@ sk_sp MultiFrameCodec::State::GetNextFrameImage( return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap, true); } else { - // Defer decoding until time of draw later on the GPU thread. Can happen + // Defer decoding until time of draw later on the raster thread. Can happen // when GL operations are currently forbidden such as in the background // on iOS. return SkImage::MakeFromBitmap(bitmap); diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 4c4b9fe9d94ee..c0b6bb9fe861c 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -57,12 +57,12 @@ enum FramePhase { /// See also [FrameTiming.buildDuration]. buildFinish, - /// When the GPU thread starts rasterizing a frame. + /// When the raster thread starts rasterizing a frame. /// /// See also [FrameTiming.rasterDuration]. rasterStart, - /// When the GPU thread finishes rasterizing a frame. + /// When the raster thread finishes rasterizing a frame. /// /// See also [FrameTiming.rasterDuration]. rasterFinish, @@ -115,7 +115,7 @@ class FrameTiming { /// {@endtemplate} Duration get buildDuration => _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); - /// The duration to rasterize the frame on the GPU thread. + /// The duration to rasterize the frame on the raster thread. /// /// {@macro dart.ui.FrameTiming.fps_smoothness_milliseconds} /// {@macro dart.ui.FrameTiming.fps_milliseconds} diff --git a/lib/web_ui/lib/src/engine/surface/scene_builder.dart b/lib/web_ui/lib/src/engine/surface/scene_builder.dart index 823a9963acddc..c61d183c83d24 100644 --- a/lib/web_ui/lib/src/engine/surface/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/surface/scene_builder.dart @@ -315,8 +315,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// controls where the statistics are displayed. /// /// enabledOptions is a bit field with the following bits defined: - /// - 0x01: displayRasterizerStatistics - show GPU thread frame time - /// - 0x02: visualizeRasterizerStatistics - graph GPU thread frame times + /// - 0x01: displayRasterizerStatistics - show raster thread frame time + /// - 0x02: visualizeRasterizerStatistics - graph raster thread frame times /// - 0x04: displayEngineStatistics - show UI thread frame time /// - 0x08: visualizeEngineStatistics - graph UI thread frame times /// Set enabledOptions to 0x0F to enable all the currently defined features. @@ -324,7 +324,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// The "UI thread" is the thread that includes all the execution of /// the main Dart isolate (the isolate that can call /// [Window.render]). The UI thread frame time is the total time - /// spent executing the [Window.onBeginFrame] callback. The "GPU + /// spent executing the [Window.onBeginFrame] callback. The "raster /// thread" is the thread (running on the CPU) that subsequently /// processes the [Scene] provided by the Dart code to turn it into /// GPU commands and send it to the GPU. diff --git a/lib/web_ui/lib/src/ui/compositing.dart b/lib/web_ui/lib/src/ui/compositing.dart index f5c5a56baad66..0bdd3441a4499 100644 --- a/lib/web_ui/lib/src/ui/compositing.dart +++ b/lib/web_ui/lib/src/ui/compositing.dart @@ -289,8 +289,8 @@ abstract class SceneBuilder { /// controls where the statistics are displayed. /// /// enabledOptions is a bit field with the following bits defined: - /// - 0x01: displayRasterizerStatistics - show GPU thread frame time - /// - 0x02: visualizeRasterizerStatistics - graph GPU thread frame times + /// - 0x01: displayRasterizerStatistics - show raster thread frame time + /// - 0x02: visualizeRasterizerStatistics - graph raster thread frame times /// - 0x04: displayEngineStatistics - show UI thread frame time /// - 0x08: visualizeEngineStatistics - graph UI thread frame times /// Set enabledOptions to 0x0F to enable all the currently defined features. @@ -298,7 +298,7 @@ abstract class SceneBuilder { /// The "UI thread" is the thread that includes all the execution of /// the main Dart isolate (the isolate that can call /// [Window.render]). The UI thread frame time is the total time - /// spent executing the [Window.onBeginFrame] callback. The "GPU + /// spent executing the [Window.onBeginFrame] callback. The "raster /// thread" is the thread (running on the CPU) that subsequently /// processes the [Scene] provided by the Dart code to turn it into /// GPU commands and send it to the GPU. diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index 0586af5dd1891..d58cc5f85908c 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -1106,12 +1106,12 @@ enum FramePhase { /// See also [FrameTiming.buildDuration]. buildFinish, - /// When the GPU thread starts rasterizing a frame. + /// When the raster thread starts rasterizing a frame. /// /// See also [FrameTiming.rasterDuration]. rasterStart, - /// When the GPU thread finishes rasterizing a frame. + /// When the raster thread finishes rasterizing a frame. /// /// See also [FrameTiming.rasterDuration]. rasterFinish, @@ -1163,7 +1163,7 @@ class FrameTiming { _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); - /// The duration to rasterize the frame on the GPU thread. + /// The duration to rasterize the frame on the raster thread. /// /// {@macro dart.ui.FrameTiming.fps_smoothness_milliseconds} /// {@macro dart.ui.FrameTiming.fps_milliseconds} diff --git a/shell/common/engine.h b/shell/common/engine.h index b44e2fae78525..0f5f0717220b1 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -210,7 +210,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// is not possible for the application to determine the total /// time it took to render a specific frame. While the /// layer-tree is constructed on the UI thread, it needs to be - /// rendering on the GPU thread. Dart code cannot execute on + /// rendering on the raster thread. Dart code cannot execute on /// this thread. So any instrumentation about the frame times /// gathered on this thread needs to be aggregated and sent back /// to the UI thread for processing in Dart. @@ -412,7 +412,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// by layer tree in the engine is 2. If both the UI and GPU /// task runner tasks finish within one frame interval, the /// pipeline depth is one. If the UI thread happens to be - /// working on a frame when the GPU thread is still not done + /// working on a frame when the raster thread is still not done /// with the previous frame, the pipeline depth is 2. When the /// pipeline depth changes from 1 to 2, animations and UI /// interactions that cause the generation of the new layer tree diff --git a/shell/common/platform_view.h b/shell/common/platform_view.h index f80f7519dcddf..15d3edac4b210 100644 --- a/shell/common/platform_view.h +++ b/shell/common/platform_view.h @@ -172,7 +172,7 @@ class PlatformView { /// Flutter layer tree. All textures must have a unique /// identifier. When the rasterizer encounters an external /// texture within its hierarchy, it gives the embedder a chance - /// to update that texture on the GPU thread before it + /// to update that texture on the raster thread before it /// composites the same on-screen. /// /// @param[in] texture The texture that is being updated by the embedder @@ -352,10 +352,11 @@ class PlatformView { //---------------------------------------------------------------------------- /// @brief Used by embedders to specify the updated viewport metrics. In - /// response to this call, on the GPU thread, the rasterizer may - /// need to be reconfigured to the updated viewport dimensions. On - /// the UI thread, the framework may need to start generating a - /// new frame for the updated viewport metrics as well. + /// response to this call, on the raster thread, the rasterizer + /// may need to be reconfigured to the updated viewport + /// dimensions. On the UI thread, the framework may need to start + /// generating a new frame for the updated viewport metrics as + /// well. /// /// @param[in] metrics The updated viewport metrics. /// @@ -488,7 +489,7 @@ class PlatformView { /// textures must have a unique identifier. When the /// rasterizer encounters an external texture within its /// hierarchy, it gives the embedder a chance to update that - /// texture on the GPU thread before it composites the same + /// texture on the raster thread before it composites the same /// on-screen. /// /// @attention This method must only be called once per texture. When the diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index fcc1e356c6399..800446c097605 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -220,8 +220,8 @@ class Rasterizer final : public SnapshotDelegate { //---------------------------------------------------------------------------- /// @brief Takes the next item from the layer tree pipeline and executes - /// the GPU thread frame workload for that pipeline item to render - /// a frame on the on-screen surface. + /// the raster thread frame workload for that pipeline item to + /// render a frame on the on-screen surface. /// /// Why does the draw call take a layer tree pipeline and not the /// layer tree directly? diff --git a/shell/common/shell.cc b/shell/common/shell.cc index d5132c6e4c38b..1b8dbb6d2f6ff 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -56,7 +56,7 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( auto shell = std::unique_ptr(new Shell(std::move(vm), task_runners, settings)); - // Create the rasterizer on the GPU thread. + // Create the rasterizer on the raster thread. std::promise> rasterizer_promise; auto rasterizer_future = rasterizer_promise.get_future(); std::promise> snapshot_delegate_promise; @@ -336,9 +336,9 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) FML_DCHECK(task_runners_.IsValid()); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - // Generate a WeakPtrFactory for use with the GPU thread. This does not need - // to wait on a latch because it can only ever be used from the GPU thread - // from this class, so we have ordering guarantees. + // Generate a WeakPtrFactory for use with the raster thread. This does not + // need to wait on a latch because it can only ever be used from the raster + // thread from this class, so we have ordering guarantees. fml::TaskRunner::RunNowOrPostTask( task_runners_.GetGPUTaskRunner(), fml::MakeCopyable([this]() mutable { this->weak_factory_gpu_ = @@ -623,13 +623,13 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { // The normal flow executed by this method is that the platform thread is // starting the sequence and waiting on the latch. Later the UI thread posts - // gpu_task to the GPU thread which signals the latch. If the GPU the and + // gpu_task to the raster thread which signals the latch. If the GPU the and // platform threads are the same this results in a deadlock as the gpu_task - // will never be posted to the plaform/gpu thread that is blocked on a latch. - // To avoid the described deadlock, if the gpu and the platform threads are - // the same, should_post_gpu_task will be false, and then instead of posting a - // task to the gpu thread, the ui thread just signals the latch and the - // platform/gpu thread follows with executing gpu_task. + // will never be posted to the plaform/raster thread that is blocked on a + // latch. To avoid the described deadlock, if the gpu and the platform threads + // are the same, should_post_gpu_task will be false, and then instead of + // posting a task to the raster thread, the ui thread just signals the latch + // and the platform/raster thread follows with executing gpu_task. bool should_post_gpu_task = task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); @@ -641,8 +641,8 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { if (engine) { engine->OnOutputSurfaceCreated(); } - // Step 2: Next, tell the GPU thread that it should create a surface for its - // rasterizer. + // Step 2: Next, tell the raster thread that it should create a surface for + // its rasterizer. if (should_post_gpu_task) { fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); } else { @@ -677,7 +677,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { if (!should_post_gpu_task) { // See comment on should_post_gpu_task, in this case the gpu_task // wasn't executed, and we just run it here as the platform thread - // is the GPU thread. + // is the raster thread. gpu_task(); } } @@ -718,13 +718,14 @@ void Shell::OnPlatformViewDestroyed() { // The normal flow executed by this method is that the platform thread is // starting the sequence and waiting on the latch. Later the UI thread posts - // gpu_task to the GPU thread triggers signaling the latch(on the IO thread). - // If the GPU the and platform threads are the same this results in a deadlock - // as the gpu_task will never be posted to the plaform/gpu thread that is - // blocked on a latch. To avoid the described deadlock, if the gpu and the - // platform threads are the same, should_post_gpu_task will be false, and then - // instead of posting a task to the gpu thread, the ui thread just signals the - // latch and the platform/gpu thread follows with executing gpu_task. + // gpu_task to the raster thread triggers signaling the latch(on the IO + // thread). If the GPU the and platform threads are the same this results in a + // deadlock as the gpu_task will never be posted to the plaform/raster thread + // that is blocked on a latch. To avoid the described deadlock, if the gpu + // and the platform threads are the same, should_post_gpu_task will be false, + // and then instead of posting a task to the raster thread, the ui thread just + // signals the latch and the platform/raster thread follows with executing + // gpu_task. bool should_post_gpu_task = task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); @@ -734,7 +735,7 @@ void Shell::OnPlatformViewDestroyed() { if (engine) { engine->OnOutputSurfaceDestroyed(); } - // Step 1: Next, tell the GPU thread that its rasterizer should suspend + // Step 1: Next, tell the raster thread that its rasterizer should suspend // access to the underlying surface. if (should_post_gpu_task) { fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); @@ -752,7 +753,7 @@ void Shell::OnPlatformViewDestroyed() { if (!should_post_gpu_task) { // See comment on should_post_gpu_task, in this case the gpu_task // wasn't executed, and we just run it here as the platform thread - // is the GPU thread. + // is the raster thread. gpu_task(); latch.Wait(); } @@ -1090,7 +1091,7 @@ void Shell::ReportTimings() { } size_t Shell::UnreportedFramesCount() const { - // Check that this is running on the GPU thread to avoid race conditions. + // Check that this is running on the raster thread to avoid race conditions. FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); FML_DCHECK(unreported_timings_.size() % FrameTiming::kCount == 0); return unreported_timings_.size() / FrameTiming::kCount; diff --git a/shell/common/shell.h b/shell/common/shell.h index 199cfe1cb44ee..d6345f115bde3 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -275,7 +275,7 @@ class Shell final : public PlatformView::Delegate, // Embedders should call this under low memory conditions to free up // internal caches used. // - // This method posts a task to the GPU threads to signal the Rasterizer to + // This method posts a task to the raster threads to signal the Rasterizer to // free resources. //---------------------------------------------------------------------------- @@ -393,7 +393,7 @@ class Shell final : public PlatformView::Delegate, std::mutex waiting_for_first_frame_mutex_; std::condition_variable waiting_for_first_frame_condition_; - // Written in the UI thread and read from the GPU thread. Hence make it + // Written in the UI thread and read from the raster thread. Hence make it // atomic. std::atomic needs_report_timings_{false}; @@ -407,10 +407,10 @@ class Shell final : public PlatformView::Delegate, std::vector unreported_timings_; // A cache of `Engine::GetDisplayRefreshRate` (only callable in the UI thread) - // so we can access it from `Rasterizer` (in the GPU thread). + // so we can access it from `Rasterizer` (in the raster thread). // // The atomic is for extra thread safety as this is written in the UI thread - // and read from the GPU thread. + // and read from the raster thread. std::atomic display_refresh_rate_ = 0.0f; // How many frames have been timed since last report. @@ -570,7 +570,7 @@ class Shell final : public PlatformView::Delegate, fml::WeakPtrFactory weak_factory_; - // For accessing the Shell via the GPU thread, necessary for various + // For accessing the Shell via the raster thread, necessary for various // rasterizer callbacks. std::unique_ptr> weak_factory_gpu_; diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index c96aece85d734..831ab18394a53 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -244,7 +244,7 @@ TaskRunners ShellTest::GetTaskRunnersForFixture() { return { "test", thread_host_.platform_thread->GetTaskRunner(), // platform - thread_host_.gpu_thread->GetTaskRunner(), // gpu + thread_host_.gpu_thread->GetTaskRunner(), // raster thread_host_.ui_thread->GetTaskRunner(), // ui thread_host_.io_thread->GetTaskRunner() // io }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index a39ea3aac6758..21787017d2d02 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -159,7 +159,7 @@ TEST_F(ShellTest, InitializeWithGPUAndPlatformThreadsTheSame) { TaskRunners task_runners( "test", thread_host.platform_thread->GetTaskRunner(), // platform - thread_host.platform_thread->GetTaskRunner(), // gpu + thread_host.platform_thread->GetTaskRunner(), // raster thread_host.ui_thread->GetTaskRunner(), // ui thread_host.io_thread->GetTaskRunner() // io ); @@ -888,7 +888,7 @@ class MockTexture : public Texture { ~MockTexture() override = default; - // Called from GPU thread. + // Called from raster thread. void Paint(SkCanvas& canvas, const SkRect& bounds, bool freeze, @@ -963,7 +963,7 @@ TEST_F(ShellTest, IsolateCanAccessPersistentIsolateData) { std::make_shared(message); TaskRunners task_runners("test", // label GetCurrentTaskRunner(), // platform - CreateNewThread(), // gpu + CreateNewThread(), // raster CreateNewThread(), // ui CreateNewThread() // io ); diff --git a/shell/gpu/gpu_surface_vulkan.cc b/shell/gpu/gpu_surface_vulkan.cc index 3d83eb8afca0e..22d67f7a777e2 100644 --- a/shell/gpu/gpu_surface_vulkan.cc +++ b/shell/gpu/gpu_surface_vulkan.cc @@ -42,9 +42,9 @@ std::unique_ptr GPUSurfaceVulkan::AcquireFrame( SurfaceFrame::SubmitCallback callback = [weak_this = weak_factory_.GetWeakPtr()](const SurfaceFrame&, SkCanvas* canvas) -> bool { - // Frames are only ever acquired on the GPU thread. This is also the thread - // on which the weak pointer factory is collected (as this instance is owned - // by the rasterizer). So this use of weak pointers is safe. + // Frames are only ever acquired on the raster thread. This is also the + // thread on which the weak pointer factory is collected (as this instance + // is owned by the rasterizer). So this use of weak pointers is safe. if (canvas == nullptr || !weak_this) { return false; } diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index cb54c2b736c48..1274969ec8b4e 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -45,7 +45,7 @@ AndroidShellHolder::AndroidShellHolder( ThreadHost::Type::IO}; } - // Detach from JNI when the UI and GPU threads exit. + // Detach from JNI when the UI and raster threads exit. auto jni_exit_task([key = thread_destruct_key_]() { FML_CHECK(pthread_setspecific(key, reinterpret_cast(1)) == 0); }); @@ -102,7 +102,7 @@ AndroidShellHolder::AndroidShellHolder( } flutter::TaskRunners task_runners(thread_label, // label platform_runner, // platform - gpu_runner, // gpu + gpu_runner, // raster ui_runner, // ui io_runner // io ); @@ -124,7 +124,7 @@ AndroidShellHolder::AndroidShellHolder( task_runners.GetGPUTaskRunner()->PostTask([]() { // Android describes -8 as "most important display threads, for // compositing the screen and retrieving input events". Conservatively - // set the GPU thread to slightly lower priority than it. + // set the raster thread to slightly lower priority than it. if (::setpriority(PRIO_PROCESS, gettid(), -5) != 0) { // Defensive fallback. Depending on the OEM, it may not be possible // to set priority to -5. diff --git a/shell/platform/darwin/common/framework/Headers/FlutterTexture.h b/shell/platform/darwin/common/framework/Headers/FlutterTexture.h index 962972025e2f4..7c3435c834f49 100644 --- a/shell/platform/darwin/common/framework/Headers/FlutterTexture.h +++ b/shell/platform/darwin/common/framework/Headers/FlutterTexture.h @@ -25,7 +25,7 @@ FLUTTER_EXPORT /** * Called when the texture is unregistered. * - * Called on the GPU thread. + * Called on the raster thread. */ @optional - (void)onTextureUnregistered:(NSObject*)texture; @@ -45,7 +45,7 @@ FLUTTER_EXPORT /** * Notifies Flutter that the content of the previously registered texture has been updated. * - * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the GPU thread. + * This will trigger a call to `-[FlutterTexture copyPixelBuffer]` on the raster thread. */ - (void)textureFrameAvailable:(int64_t)textureId; /** diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index c267518929685..354d482c283ae 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -462,7 +462,7 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { flutter::TaskRunners task_runners(threadLabel.UTF8String, // label fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform - fml::MessageLoop::GetCurrent().GetTaskRunner(), // gpu + fml::MessageLoop::GetCurrent().GetTaskRunner(), // raster _threadHost.ui_thread->GetTaskRunner(), // ui _threadHost.io_thread->GetTaskRunner() // io ); @@ -476,7 +476,7 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { } else { flutter::TaskRunners task_runners(threadLabel.UTF8String, // label fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform - _threadHost.gpu_thread->GetTaskRunner(), // gpu + _threadHost.gpu_thread->GetTaskRunner(), // raster _threadHost.ui_thread->GetTaskRunner(), // ui _threadHost.io_thread->GetTaskRunner() // io ); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 6e7e8c495828d..e1f76da1d5df4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -453,7 +453,7 @@ - (void)installFirstFrameCallback { platformTaskRunner = [_engine.get() platformTaskRunner], gpuTaskRunner = [_engine.get() GPUTaskRunner]]() { FML_DCHECK(gpuTaskRunner->RunsTasksOnCurrentThread()); - // Get callback on GPU thread and jump back to platform thread. + // Get callback on raster thread and jump back to platform thread. platformTaskRunner->PostTask([weakSelf]() { fml::scoped_nsobject flutterViewController( [(FlutterViewController*)weakSelf.get() retain]); @@ -544,7 +544,8 @@ - (void)setFlutterViewDidRenderCallback:(void (^)(void))callback { #pragma mark - Surface creation and teardown updates - (void)surfaceUpdated:(BOOL)appeared { - // NotifyCreated/NotifyDestroyed are synchronous and require hops between the UI and GPU thread. + // NotifyCreated/NotifyDestroyed are synchronous and require hops between the UI and raster + // thread. if (appeared) { [self installFirstFrameCallback]; [_engine.get() platformViewsController] -> SetFlutterView(_flutterView.get()); diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index 4bd76980f8879..0412acd722e98 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -68,7 +68,7 @@ class PlatformViewIOS final : public PlatformView { fml::WeakPtr owner_controller_; // Since the `ios_surface_` is created on the platform thread but - // used on the GPU thread we need to protect it with a mutex. + // used on the raster thread we need to protect it with a mutex. std::mutex ios_surface_mutex_; std::unique_ptr ios_surface_; std::shared_ptr ios_context_; diff --git a/shell/platform/embedder/embedder_thread_host.cc b/shell/platform/embedder/embedder_thread_host.cc index d5a1dc58c92ef..430434bb15286 100644 --- a/shell/platform/embedder/embedder_thread_host.cc +++ b/shell/platform/embedder/embedder_thread_host.cc @@ -173,7 +173,7 @@ EmbedderThreadHost::CreateEmbedderManagedThreadHost( flutter::TaskRunners task_runners( kFlutterThreadName, platform_task_runner, // platform - render_task_runner, // gpu + render_task_runner, // raster thread_host.ui_thread->GetTaskRunner(), // ui (always engine managed) thread_host.io_thread->GetTaskRunner() // io (always engine managed) ); @@ -220,7 +220,7 @@ EmbedderThreadHost::CreateEngineManagedThreadHost() { flutter::TaskRunners task_runners( kFlutterThreadName, platform_task_runner, // platform - thread_host.gpu_thread->GetTaskRunner(), // gpu + thread_host.gpu_thread->GetTaskRunner(), // raster thread_host.ui_thread->GetTaskRunner(), // ui thread_host.io_thread->GetTaskRunner() // io ); diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 8e2cd17e05170..3164fd712b76a 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1494,8 +1494,8 @@ TEST_F(EmbedderTest, } //------------------------------------------------------------------------------ -/// Custom compositor must play nicely with a custom task runner. The GPU thread -/// merging mechanism must not interfere with the custom compositor. +/// Custom compositor must play nicely with a custom task runner. The raster +/// thread merging mechanism must not interfere with the custom compositor. /// TEST_F(EmbedderTest, CustomCompositorMustWorkWithCustomTaskRunner) { auto& context = GetEmbedderContext(); diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index eb850aa52aa3d..1b79dd6534fbc 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -154,7 +154,7 @@ Engine::Engine(Delegate& delegate, ); }); - // Session can be terminated on the GPU thread, but we must terminate + // Session can be terminated on the raster thread, but we must terminate // ourselves on the platform thread. // // This handles the fidl error callback when the Session connection is @@ -175,7 +175,7 @@ Engine::Engine(Delegate& delegate, const flutter::TaskRunners task_runners( thread_label_, // Dart thread labels CreateFMLTaskRunner(async_get_default_dispatcher()), // platform - CreateFMLTaskRunner(threads_[0]->dispatcher()), // gpu + CreateFMLTaskRunner(threads_[0]->dispatcher()), // raster CreateFMLTaskRunner(threads_[1]->dispatcher()), // ui CreateFMLTaskRunner(threads_[2]->dispatcher()) // io ); diff --git a/shell/platform/fuchsia/flutter/session_connection.h b/shell/platform/fuchsia/flutter/session_connection.h index e0855753c6b17..54ba846ee8974 100644 --- a/shell/platform/fuchsia/flutter/session_connection.h +++ b/shell/platform/fuchsia/flutter/session_connection.h @@ -27,7 +27,7 @@ namespace flutter_runner { using on_frame_presented_event = std::function; -// The component residing on the GPU thread that is responsible for +// The component residing on the raster thread that is responsible for // maintaining the Scenic session connection and presenting node updates. class SessionConnection final { public: diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index 66220f079b3e5..c1ca54be1baad 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -12,8 +12,8 @@ namespace flutter_runner { // The interface between the Flutter rasterizer and the underlying platform. May -// be constructed on any thread but will be used by the engine only on the GPU -// thread. +// be constructed on any thread but will be used by the engine only on the +// raster thread. class Surface final : public flutter::Surface { public: Surface(std::string debug_label); diff --git a/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc index f1bcfc12ba350..83e71d64c6109 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc +++ b/shell/platform/fuchsia/flutter/vsync_waiter_unittests.cc @@ -62,7 +62,7 @@ TEST_F(VsyncWaiterTest, AwaitVsync) { "VsyncWaiterTests", // Dart thread labels flutter_runner::CreateFMLTaskRunner( async_get_default_dispatcher()), // platform - flutter_runner::CreateFMLTaskRunner(threads[0]->dispatcher()), // gpu + flutter_runner::CreateFMLTaskRunner(threads[0]->dispatcher()), // raster flutter_runner::CreateFMLTaskRunner(threads[1]->dispatcher()), // ui flutter_runner::CreateFMLTaskRunner(threads[2]->dispatcher()) // io ); diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 3f31fd6cefe25..ce0be3b012c9b 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -128,7 +128,7 @@ int RunTester(const flutter::Settings& settings, const flutter::TaskRunners task_runners(thread_label, // dart thread label platform_task_runner, // platform - gpu_task_runner, // gpu + gpu_task_runner, // raster ui_task_runner, // ui io_task_runner // io ); From 80476a4c9c01bcef7e8daaff01b29a1beb7006d4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 03:56:03 -0400 Subject: [PATCH 149/386] Roll src/third_party/skia b6a99b7b9acb..ee2ec6de995b (9 commits) (#17343) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6d83f9d531fd4..bce5196d8d9a4 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'b6a99b7b9acbbaae466fd73516edf9019834e5fc', + 'skia_revision': 'ee2ec6de995b8f81c8d26f236aee1b6f5c97efd6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index e96ec82915f35..2e34c9d10ead0 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: d038349d8e5836ab069607729c429932 +Signature: a2d9963d8778c7b126080961269c7359 UNUSED LICENSES: @@ -5637,6 +5637,7 @@ FILE: ../../../third_party/skia/src/core/SkVerticesPriv.h FILE: ../../../third_party/skia/src/gpu/GrDynamicAtlas.cpp FILE: ../../../third_party/skia/src/gpu/GrDynamicAtlas.h FILE: ../../../third_party/skia/src/gpu/GrEagerVertexAllocator.h +FILE: ../../../third_party/skia/src/gpu/ccpr/GrAutoMapVertexBuffer.h FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrDrawAtlasPathOp.cpp From 444096e8ed7d4976ca683f19e2417f3a6df2a16b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 04:11:02 -0400 Subject: [PATCH 150/386] Roll fuchsia/sdk/core/linux-amd64 from jHDPP... to nK6R0... (#17341) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index bce5196d8d9a4..35dc5881b3a7d 100644 --- a/DEPS +++ b/DEPS @@ -556,7 +556,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'jHDPP4XNSmH-VqBjuMtIMYWvpboXqbqAf4oP4FT-KC0C' + 'version': 'nK6R0-Z1LhGfmogq-nAUTlOomxkKUCV00iPPkXSgVgUC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 6a8f36f115d5a..3009d8b178c37 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 617bc193a64c9a737e35dd89a3c62975 +Signature: 9cd367155310be6c188125ba3d3e842d UNUSED LICENSES: @@ -464,6 +464,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castwindow/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.runner/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/meta.json @@ -1378,6 +1379,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/cast_setup.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/cast_system_info.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castwindow/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.runner/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/meta.json @@ -2202,6 +2204,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castconfig/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsysteminfo/meta.json +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castwindow/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.cobalt/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component.runner/meta.json FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/meta.json @@ -3265,6 +3268,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/device_watcher.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.camera3/stream.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castremotecontrol/remote_control.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castsetup/server.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.castwindow/window.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/constants.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/error.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.component/types.fidl From c71edb143f1fe07e558c0a57b4a23bc0f38193e3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 05:26:02 -0400 Subject: [PATCH 151/386] Roll src/third_party/dart bc53ecefae52..f6acefa078be (4 commits) (#17344) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 35dc5881b3a7d..85eb9d60d6183 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'bc53ecefae523208155cea798c13dd430bcc30e2', + 'dart_revision': 'f6acefa078be0cf448f18697a025684fbf5d0ea0', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index b0f854e1e2a54..7feaa9a2d08d9 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: ec9289c8fd6a8d089a9d6c507cdcfc31 +Signature: da1bde54103c07da0fd99fa9e639a77b UNUSED LICENSES: From 8fa7214d3ac3a3635d50c9737cf739cb5c850ba4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 10:01:03 -0400 Subject: [PATCH 152/386] Roll src/third_party/skia ee2ec6de995b..95fc5a325168 (2 commits) (#17346) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 85eb9d60d6183..f6f6f8bfd3d67 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'ee2ec6de995b8f81c8d26f236aee1b6f5c97efd6', + 'skia_revision': '95fc5a32516866bd2d7aa8187995bcd542ab1687', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2e34c9d10ead0..67af4c0635c0b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: a2d9963d8778c7b126080961269c7359 +Signature: 175bfeee6ea844cd3d27fe807035f745 UNUSED LICENSES: From 32742df3dd2ae911725f44048b3384fb427e86ec Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 10:36:04 -0400 Subject: [PATCH 153/386] Roll src/third_party/dart f6acefa078be..1402e8e1a42d (1 commits) (#17347) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f6f6f8bfd3d67..1585e004394ac 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'f6acefa078be0cf448f18697a025684fbf5d0ea0', + 'dart_revision': '1402e8e1a42d6be6ea370346677cd57093d4be03', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From f0caace4fdef77ec2366f8b6c8e84bce8cd8eede Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 26 Mar 2020 09:37:14 -0700 Subject: [PATCH 154/386] [web] Profile text layout and forward data to macrobenchmarks (#17276) --- ci/licenses_golden/licenses_flutter | 1 + lib/web_ui/lib/src/engine.dart | 5 + lib/web_ui/lib/src/engine/profiler.dart | 72 ++++++++++++++ lib/web_ui/lib/src/engine/text/paragraph.dart | 9 ++ lib/web_ui/test/canvas_test.dart | 1 + lib/web_ui/test/engine/profiler_test.dart | 99 +++++++++++++++++++ lib/web_ui/test/paragraph_builder_test.dart | 1 + 7 files changed, 188 insertions(+) create mode 100644 lib/web_ui/lib/src/engine/profiler.dart create mode 100644 lib/web_ui/test/engine/profiler_test.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ebc6f87e28903..f9cfcab303213 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -448,6 +448,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/platform_views.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/plugins.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_binding.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/pointer_converter.dart +FILE: ../../../flutter/lib/web_ui/lib/src/engine/profiler.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/render_vertices.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index f735c78ea5612..9af6c4f7887f2 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -69,6 +69,7 @@ part 'engine/platform_views.dart'; part 'engine/plugins.dart'; part 'engine/pointer_binding.dart'; part 'engine/pointer_converter.dart'; +part 'engine/profiler.dart'; part 'engine/render_vertices.dart'; part 'engine/rrect_renderer.dart'; part 'engine/semantics/accessibility.dart'; @@ -164,6 +165,10 @@ void webOnlyInitializeEngine() { WebExperiments.ensureInitialized(); + if (Profiler.isBenchmarkMode) { + Profiler.ensureInitialized(); + } + bool waitingForAnimation = false; ui.webOnlyScheduleFrameCallback = () { // We're asked to schedule a frame and call `frameHandler` when the frame diff --git a/lib/web_ui/lib/src/engine/profiler.dart b/lib/web_ui/lib/src/engine/profiler.dart new file mode 100644 index 0000000000000..383f480917042 --- /dev/null +++ b/lib/web_ui/lib/src/engine/profiler.dart @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +part of engine; + +typedef OnBenchmark = void Function(String name, num value); + +/// The purpose of this class is to facilitate communication of +/// profiling/benchmark data to the outside world (e.g. a macrobenchmark that's +/// running a flutter app). +/// +/// To use the [Profiler]: +/// +/// 1. Set the environment variable `FLUTTER_WEB_ENABLE_PROFILING` to true. +/// +/// 2. Using JS interop, assign a listener function to +/// `window._flutter_internal_on_benchmark` in the browser. +/// +/// The listener function will be called every time a new benchmark number is +/// calculated. The signature is `Function(String name, num value)`. +class Profiler { + Profiler._() { + _checkBenchmarkMode(); + } + + static bool isBenchmarkMode = const bool.fromEnvironment( + 'FLUTTER_WEB_ENABLE_PROFILING', + defaultValue: true, + ); + + static Profiler ensureInitialized() { + _checkBenchmarkMode(); + return Profiler._instance ??= Profiler._(); + } + + static Profiler get instance { + _checkBenchmarkMode(); + if (_instance == null) { + throw Exception( + 'Profiler has not been properly initialized. ' + 'Make sure Profiler.ensureInitialized() is being called before you ' + 'access Profiler.instance', + ); + } + return _instance; + } + + static Profiler _instance; + + static void _checkBenchmarkMode() { + if (!isBenchmarkMode) { + throw Exception( + 'Cannot use Profiler unless benchmark mode is enabled. ' + 'You can enable it by setting the `FLUTTER_WEB_ENABLE_PROFILING` ' + 'environment variable to true.', + ); + } + } + + /// Used to send benchmark data to whoever is listening to them. + void benchmark(String name, num value) { + _checkBenchmarkMode(); + + final OnBenchmark onBenchmark = + js_util.getProperty(html.window, '_flutter_internal_on_benchmark'); + if (onBenchmark != null) { + onBenchmark(name, value); + } + } +} diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index 83ec07b875f25..3dbc6fbecafe1 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -255,7 +255,16 @@ class EngineParagraph implements ui.Paragraph { return; } + Stopwatch stopwatch; + if (Profiler.isBenchmarkMode) { + stopwatch = Stopwatch()..start(); + } _measurementResult = _measurementService.measure(this, constraints); + if (Profiler.isBenchmarkMode) { + stopwatch.stop(); + Profiler.instance.benchmark('text_layout', stopwatch.elapsedMicroseconds); + } + _lastUsedConstraints = constraints; if (_geometricStyle.maxLines != null) { diff --git a/lib/web_ui/test/canvas_test.dart b/lib/web_ui/test/canvas_test.dart index 3a63ee87085d7..ce9d1499caca2 100644 --- a/lib/web_ui/test/canvas_test.dart +++ b/lib/web_ui/test/canvas_test.dart @@ -13,6 +13,7 @@ import 'mock_engine_canvas.dart'; void main() { setUpAll(() { WebExperiments.ensureInitialized(); + Profiler.ensureInitialized(); }); group('EngineCanvas', () { diff --git a/lib/web_ui/test/engine/profiler_test.dart b/lib/web_ui/test/engine/profiler_test.dart new file mode 100644 index 0000000000000..58f12d17cf758 --- /dev/null +++ b/lib/web_ui/test/engine/profiler_test.dart @@ -0,0 +1,99 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +import 'dart:html' as html; +import 'dart:js_util' as js_util; + +import 'package:test/test.dart'; +import 'package:ui/src/engine.dart'; +import 'package:ui/ui.dart'; + +void main() { + setUp(() { + Profiler.isBenchmarkMode = true; + Profiler.ensureInitialized(); + }); + + tearDown(() { + jsOnBenchmark(null); + Profiler.isBenchmarkMode = false; + }); + + test('works when there is no listener', () { + expect(() => Profiler.instance.benchmark('foo', 123), returnsNormally); + }); + + test('can listen to benchmarks', () { + final List data = []; + jsOnBenchmark((String name, num value) { + data.add(BenchmarkDatapoint(name, value)); + }); + + Profiler.instance.benchmark('foo', 123); + expect(data, [BenchmarkDatapoint('foo', 123)]); + data.clear(); + + Profiler.instance.benchmark('bar', 0.0125); + expect(data, [BenchmarkDatapoint('bar', 0.0125)]); + data.clear(); + + // Remove listener and make sure nothing breaks and the data isn't being + // sent to the old callback anymore. + jsOnBenchmark(null); + expect(() => Profiler.instance.benchmark('baz', 99.999), returnsNormally); + expect(data, isEmpty); + }); + + test('throws on wrong listener type', () { + final List data = []; + + // Wrong callback signature. + jsOnBenchmark((num value) { + data.add(BenchmarkDatapoint('bad', value)); + }); + expect( + () => Profiler.instance.benchmark('foo', 123), + throwsA(isA()), + ); + expect(data, isEmpty); + + // Not even a callback. + jsOnBenchmark('string'); + expect( + () => Profiler.instance.benchmark('foo', 123), + throwsA(isA()), + ); + }); +} + +class BenchmarkDatapoint { + BenchmarkDatapoint(this.name, this.value); + + final String name; + final num value; + + @override + int get hashCode => hashValues(name, value); + + @override + operator ==(dynamic other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + return name == other.name && value == other.value; + } + + @override + String toString() { + return '$runtimeType("$name", $value)'; + } +} + +void jsOnBenchmark(dynamic listener) { + js_util.setProperty(html.window, '_flutter_internal_on_benchmark', listener); +} diff --git a/lib/web_ui/test/paragraph_builder_test.dart b/lib/web_ui/test/paragraph_builder_test.dart index 54a3bcf53b046..6aa9d0015bef6 100644 --- a/lib/web_ui/test/paragraph_builder_test.dart +++ b/lib/web_ui/test/paragraph_builder_test.dart @@ -11,6 +11,7 @@ import 'package:test/test.dart'; void main() { setUpAll(() { WebExperiments.ensureInitialized(); + Profiler.ensureInitialized(); }); test('Should be able to build and layout a paragraph', () { From f44e8a42d0b8bcaf7ebe417cb0e88c54aa92103a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 13:21:01 -0400 Subject: [PATCH 155/386] Roll src/third_party/skia 95fc5a325168..265de3a93c6e (1 commits) (#17348) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1585e004394ac..7d9b72f3c24f0 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '95fc5a32516866bd2d7aa8187995bcd542ab1687', + 'skia_revision': '265de3a93c6ec3f21e1b1e01e89e4a4c712f9d6c', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 67af4c0635c0b..9fb03cb11f712 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 175bfeee6ea844cd3d27fe807035f745 +Signature: 107380be15d2ea8a6af16f32e612af59 UNUSED LICENSES: From b2bdeb3f0f1683f3e0562f491b5e316240dfbc2c Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 26 Mar 2020 13:26:02 -0400 Subject: [PATCH 156/386] Roll fuchsia/sdk/core/mac-amd64 from 6zho5... to d4Bft... (#17349) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 7d9b72f3c24f0..1cdade9a8b0a4 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '6zho5oUADpegao7qM81YbY1frvdweFGrpMtA0qj2uhsC' + 'version': 'd4Bftt0zPSH0X7YiI1VyGEN_i7LM-HCEexU4Qn3OmxIC' } ], 'condition': 'host_os == "mac"', From cda57c14cb4e3039a5eae78e805a634859b1c2d9 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 26 Mar 2020 13:11:54 -0700 Subject: [PATCH 157/386] Do not drop toString for enums (#17352) --- flutter_frontend_server/lib/server.dart | 1 + .../test/fixtures/lib/main.dart | 1 + .../test/to_string_test.dart | 21 +++++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/flutter_frontend_server/lib/server.dart b/flutter_frontend_server/lib/server.dart index 0c50557a71ecd..e0e8bf8e4dff6 100644 --- a/flutter_frontend_server/lib/server.dart +++ b/flutter_frontend_server/lib/server.dart @@ -224,6 +224,7 @@ class ToStringVisitor extends RecursiveVisitor { node.enclosingLibrary != null && !node.isStatic && !node.isAbstract && + !node.enclosingClass.isEnum && _isInTargetPackage(node) && !_hasKeepAnnotation(node) ) { diff --git a/flutter_frontend_server/test/fixtures/lib/main.dart b/flutter_frontend_server/test/fixtures/lib/main.dart index bfa11f7d1bc5f..fdd3bb9045c79 100644 --- a/flutter_frontend_server/test/fixtures/lib/main.dart +++ b/flutter_frontend_server/test/fixtures/lib/main.dart @@ -9,6 +9,7 @@ void main() { final Paint paint = Paint()..color = Color(0xFFFFFFFF); print(jsonEncode({ 'Paint.toString': paint.toString(), + 'Brightness.toString': Brightness.dark.toString(), 'Foo.toString': Foo().toString(), 'Keep.toString': Keep().toString(), })); diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart index ac70b23697ad3..3ee7570973a67 100644 --- a/flutter_frontend_server/test/to_string_test.dart +++ b/flutter_frontend_server/test/to_string_test.dart @@ -121,6 +121,25 @@ void main(List args) async { verifyNever(statement.replaceWith(any)); }); + test('ToStringVisitor ignores enum toString', () { + final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); + final MockProcedure procedure = MockProcedure(); + final MockFunctionNode function = MockFunctionNode(); + final MockStatement statement = MockStatement(); + final Library library = Library(Uri.parse('package:some_package/src/blah.dart')); + when(procedure.function).thenReturn(function); + when(procedure.name).thenReturn(Name('toString')); + when(procedure.annotations).thenReturn(const []); + when(procedure.enclosingLibrary).thenReturn(library); + when(procedure.enclosingClass).thenReturn(Class()..isEnum = true); + when(procedure.isAbstract).thenReturn(false); + when(procedure.isStatic).thenReturn(false); + when(function.body).thenReturn(statement); + + visitor.visitProcedure(procedure); + verifyNever(statement.replaceWith(any)); + }); + test('ToStringVisitor ignores non-specified libraries', () { final ToStringVisitor visitor = ToStringVisitor(uiAndFlutter); final MockProcedure procedure = MockProcedure(); @@ -254,6 +273,7 @@ void main(List args) async { expect( runResult.stdout.trim(), '{"Paint.toString":"Paint(Color(0xffffffff))",' + '"Brightness.toString":"Brightness.dark",' '"Foo.toString":"I am a Foo",' '"Keep.toString":"I am a Keep"}', ); @@ -275,6 +295,7 @@ void main(List args) async { expect( runResult.stdout.trim(), '{"Paint.toString":"Instance of \'Paint\'",' + '"Brightness.toString":"Brightness.dark",' '"Foo.toString":"Instance of \'Foo\'",' '"Keep.toString":"I am a Keep"}', ); From 44fc6760d770b06677b896c5e09ee9c0312da200 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Thu, 26 Mar 2020 14:01:36 -0700 Subject: [PATCH 158/386] [web] improving felt: running target unit tests, cleaning up used proccesses (#17339) * improving felt: running target unit tests, cleaning up used proccesses * removing null check --- lib/web_ui/dev/felt.dart | 1 + lib/web_ui/dev/integration_tests_manager.dart | 15 ++++---- lib/web_ui/dev/test_runner.dart | 35 +++++++++++++------ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/lib/web_ui/dev/felt.dart b/lib/web_ui/dev/felt.dart index fd4d2f6c73645..9b860872f3387 100644 --- a/lib/web_ui/dev/felt.dart +++ b/lib/web_ui/dev/felt.dart @@ -35,6 +35,7 @@ void main(List args) async { final bool result = await runner.run(args); if (result == false) { print('Sub-command returned false: `${args.join(' ')}`'); + _cleanup(); io.exit(1); } } on UsageException catch (e) { diff --git a/lib/web_ui/dev/integration_tests_manager.dart b/lib/web_ui/dev/integration_tests_manager.dart index 6e5457da4fe0b..8a7967784c966 100644 --- a/lib/web_ui/dev/integration_tests_manager.dart +++ b/lib/web_ui/dev/integration_tests_manager.dart @@ -5,7 +5,6 @@ import 'dart:io' as io; import 'package:path/path.dart' as pathlib; import 'package:web_driver_installer/chrome_driver_installer.dart'; -import 'package:yaml/yaml.dart'; import 'chrome_installer.dart'; import 'common.dart'; @@ -21,8 +20,7 @@ class IntegrationTestsManager { /// It usually changes with each the browser version changes. /// A better solution would be installing the browser and the driver at the /// same time. - // TODO(nurhan): change the web installers to install driver and the browser - // at the same time. + // TODO(nurhan): https://github.com/flutter/flutter/issues/53179. final io.Directory _browserDriverDir; /// This is the parent directory for all drivers. @@ -32,10 +30,10 @@ class IntegrationTestsManager { final io.Directory _drivers; IntegrationTestsManager(this._browser) - : this._browserDriverDir = io.Directory( - pathlib.join(environment.webUiRootDir.path, 'drivers', _browser)), + : this._browserDriverDir = io.Directory(pathlib.join( + environment.webUiDartToolDir.path, 'drivers', _browser)), this._drivers = io.Directory( - pathlib.join(environment.webUiRootDir.path, 'drivers')); + pathlib.join(environment.webUiDartToolDir.path, 'drivers')); Future runTests() async { if (_browser != 'chrome') { @@ -77,6 +75,7 @@ class IntegrationTestsManager { startProcess( './chromedriver/chromedriver', ['--port=4444'], + workingDirectory: io.Directory.current.path ); print('INFO: Driver started'); } @@ -89,12 +88,16 @@ class IntegrationTestsManager { _browserDriverDir.createSync(recursive: true); temporaryDirectories.add(_drivers); + io.Directory temp = io.Directory.current; + io.Directory.current = _browserDriverDir; + // TODO(nurhan): https://github.com/flutter/flutter/issues/53179 final String chromeDriverVersion = await queryChromeDriverVersion(); ChromeDriverInstaller chromeDriverInstaller = ChromeDriverInstaller.withVersion(chromeDriverVersion); await chromeDriverInstaller.install(alwaysInstall: true); await _runDriver(); + io.Directory.current = temp; } /// Runs all the web tests under e2e_tests/web. diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index fa66b05954d3c..8ca3fa13e3530 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -115,13 +115,18 @@ class TestCommand extends Command { case TestTypesRequested.integration: return runIntegrationTests(); case TestTypesRequested.all: - bool integrationTestResult = await runIntegrationTests(); - bool unitTestResult = await runUnitTests(); - if (integrationTestResult != unitTestResult) { - print('Tests run. Integration tests passed: $integrationTestResult ' - 'unit tests passed: $unitTestResult'); + // TODO(nurhan): https://github.com/flutter/flutter/issues/53322 + if (runAllTests) { + bool integrationTestResult = await runIntegrationTests(); + bool unitTestResult = await runUnitTests(); + if (integrationTestResult != unitTestResult) { + print('Tests run. Integration tests passed: $integrationTestResult ' + 'unit tests passed: $unitTestResult'); + } + return integrationTestResult && unitTestResult; + } else { + return await runUnitTests(); } - return integrationTestResult && unitTestResult; } return false; } @@ -145,13 +150,11 @@ class TestCommand extends Command { await _runPubGet(); } - final List targets = - this.targets.map((t) => FilePath.fromCwd(t)).toList(); - await _buildTests(targets: targets); - if (targets.isEmpty) { + await _buildTests(targets: targetFiles); + if (runAllTests) { await _runAllTests(); } else { - await _runTargetTests(targets); + await _runTargetTests(targetFiles); } return true; } @@ -165,6 +168,16 @@ class TestCommand extends Command { /// Paths to targets to run, e.g. a single test. List get targets => argResults.rest; + /// The target test files to run. + /// + /// The value can be null if the developer prefers to run all the tests. + List get targetFiles => (targets.isEmpty) + ? null + : targets.map((t) => FilePath.fromCwd(t)).toList(); + + /// Whether all tests should run. + bool get runAllTests => targets.isEmpty; + String get browser => argResults['browser']; bool get isChrome => argResults['browser'] == 'chrome'; From 5389159643fa26342580208b6a9de547ef4de406 Mon Sep 17 00:00:00 2001 From: Yegor Date: Thu, 26 Mar 2020 14:38:59 -0700 Subject: [PATCH 159/386] add limited analysis options (#17332) add limited analysis options --- .cirrus.yml | 10 +++ lib/web_ui/analysis_options.yaml | 88 ++++++++++++++++++- lib/web_ui/dev/browser.dart | 2 +- lib/web_ui/dev/firefox_installer_test.dart | 2 +- lib/web_ui/dev/test_platform.dart | 2 +- .../lib/src/engine/compositor/vertices.dart | 2 +- .../lib/src/engine/surface/path_metrics.dart | 1 - lib/web_ui/lib/src/engine/window.dart | 2 - lib/web_ui/lib/src/ui/painting.dart | 2 - lib/web_ui/lib/src/ui/text.dart | 8 -- .../engine/image/html_image_codec_test.dart | 2 +- .../engine/canvas_context_test.dart | 1 - .../engine/path_metrics_test.dart | 11 --- .../engine/text_style_golden_test.dart | 1 - lib/web_ui/test/text/measurement_test.dart | 1 + 15 files changed, 103 insertions(+), 32 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 77d8b81ca4a6f..7b954671a7aeb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -126,6 +126,16 @@ task: - name: web_tests-7_last-linux # last Web shard must end with _last << : *WEB_SHARD_TEMPLATE + - name: web_engine_analysis + compile_host_script: | + cd $ENGINE_PATH/src + ./flutter/tools/gn --unoptimized --full-dart-sdk + ninja -C out/host_debug_unopt + script: + - cd $ENGINE_PATH/src/flutter/lib/web_ui + - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/pub get + - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/dartanalyzer --fatal-warnings --fatal-hints dev/ lib/ test/ tool/ + - name: web_engine_integration_test_linux compile_host_script: | cd $ENGINE_PATH/src diff --git a/lib/web_ui/analysis_options.yaml b/lib/web_ui/analysis_options.yaml index 6892f49dc726b..7c46e201fff84 100644 --- a/lib/web_ui/analysis_options.yaml +++ b/lib/web_ui/analysis_options.yaml @@ -1 +1,87 @@ -# Intentionally kept empty. \ No newline at end of file +# This is copy of the root analysis_options.yaml. As we clean up the Web code, +# we'll be uncommenting rules and gradually fix the code. When all rules are +# uncommented, we'll delete this file and simply inherit the root options. + +analyzer: + # TODO(uncomment) strong-mode: + # TODO(uncomment) implicit-casts: false + # TODO(uncomment) implicit-dynamic: false + errors: + missing_required_param: warning + missing_return: warning + native_function_body_in_non_sdk_code: ignore + todo: ignore + +linter: + rules: + - always_declare_return_types + # TODO(uncomment) - always_put_control_body_on_new_line + # TODO(uncomment) - always_specify_types + # TODO(uncomment) - annotate_overrides + # TODO(uncomment) - avoid_classes_with_only_static_members + # TODO(uncomment) - avoid_empty_else + # TODO(uncomment) - avoid_function_literals_in_foreach_calls + # TODO(uncomment) - avoid_init_to_null + # TODO(uncomment) - avoid_null_checks_in_equality_operators + # TODO(uncomment) - avoid_relative_lib_imports + # TODO(uncomment) - avoid_renaming_method_parameters + # TODO(uncomment) - avoid_return_types_on_setters + # TODO(uncomment) - avoid_slow_async_io + # TODO(uncomment) - await_only_futures + # TODO(uncomment) - camel_case_types + # TODO(uncomment) - cancel_subscriptions + # TODO(uncomment) - control_flow_in_finally + # TODO(uncomment) - directives_ordering + # TODO(uncomment) - empty_catches + # TODO(uncomment) - empty_constructor_bodies + # TODO(uncomment) - empty_statements + # TODO(uncomment) - hash_and_equals + # TODO(uncomment) - implementation_imports + # TODO(uncomment) - iterable_contains_unrelated_type + # TODO(uncomment) - library_names + # TODO(uncomment) - library_prefixes + # TODO(uncomment) - list_remove_unrelated_type + # TODO(uncomment) - no_adjacent_strings_in_list + # TODO(uncomment) - no_duplicate_case_values + # TODO(uncomment) - non_constant_identifier_names + # TODO(uncomment) - overridden_fields + # TODO(uncomment) - package_api_docs + # TODO(uncomment) - package_names + # TODO(uncomment) - package_prefixed_library_names + # TODO(uncomment) - prefer_adjacent_string_concatenation + # TODO(uncomment) - prefer_asserts_in_initializer_lists + # TODO(uncomment) - prefer_collection_literals + # TODO(uncomment) - prefer_conditional_assignment + # TODO(uncomment) - prefer_const_constructors + # TODO(uncomment) - prefer_const_constructors_in_immutables + # TODO(uncomment) - prefer_const_declarations + # TODO(uncomment) - prefer_const_literals_to_create_immutables + # TODO(uncomment) - prefer_contains + # TODO(uncomment) - prefer_equal_for_default_values + # TODO(uncomment) - prefer_final_locals + # TODO(uncomment) - prefer_foreach + # TODO(uncomment) - prefer_generic_function_type_aliases + # TODO(uncomment) - prefer_initializing_formals + # TODO(uncomment) - prefer_is_empty + # TODO(uncomment) - prefer_is_not_empty + # TODO(uncomment) - prefer_single_quotes + # TODO(uncomment) - prefer_typing_uninitialized_variables + # TODO(uncomment) - public_member_api_docs + # TODO(uncomment) - recursive_getters + # TODO(uncomment) - slash_for_doc_comments + # TODO(uncomment) - sort_unnamed_constructors_first + # TODO(uncomment) - test_types_in_equals + # TODO(uncomment) - throw_in_finally + # TODO(uncomment) - type_init_formals + # TODO(uncomment) - unnecessary_brace_in_string_interps + # TODO(uncomment) - unnecessary_const + # TODO(uncomment) - unnecessary_getters_setters + # TODO(uncomment) - unnecessary_new + # TODO(uncomment) - unnecessary_null_aware_assignments + # TODO(uncomment) - unnecessary_null_in_if_null_operators + # TODO(uncomment) - unnecessary_overrides + # TODO(uncomment) - unnecessary_parenthesis + # TODO(uncomment) - unnecessary_this + # TODO(uncomment) - unrelated_type_equality_checks + # TODO(uncomment) - use_rethrow_when_possible + # TODO(uncomment) - valid_regexps diff --git a/lib/web_ui/dev/browser.dart b/lib/web_ui/dev/browser.dart index 8c4c736e99aab..989609cd8a5bf 100644 --- a/lib/web_ui/dev/browser.dart +++ b/lib/web_ui/dev/browser.dart @@ -72,7 +72,7 @@ abstract class Browser { _processCompleter.complete(process); var output = Uint8Buffer(); - drainOutput(Stream> stream) { + void drainOutput(Stream> stream) { try { _ioSubscriptions .add(stream.listen(output.addAll, cancelOnError: true)); diff --git a/lib/web_ui/dev/firefox_installer_test.dart b/lib/web_ui/dev/firefox_installer_test.dart index 2d687d7be1c6b..fc18277f53b14 100644 --- a/lib/web_ui/dev/firefox_installer_test.dart +++ b/lib/web_ui/dev/firefox_installer_test.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// @dart = 2.6 @TestOn('vm && linux') -// @dart = 2.6 import 'dart:io' as io; import 'package:path/path.dart' as path; diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 6b4663d5d4a91..1e5aa3c79b7b8 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -744,7 +744,7 @@ class BrowserManager { var suiteID = _suiteID++; RunnerSuiteController controller; - closeIframe() { + void closeIframe() { if (_closed) return; _controllers.remove(controller); _channel.sink.add({'command': 'closeSuite', 'id': suiteID}); diff --git a/lib/web_ui/lib/src/engine/compositor/vertices.dart b/lib/web_ui/lib/src/engine/compositor/vertices.dart index 67164c498411c..b9d43ba6d262b 100644 --- a/lib/web_ui/lib/src/engine/compositor/vertices.dart +++ b/lib/web_ui/lib/src/engine/compositor/vertices.dart @@ -116,7 +116,7 @@ class SkVertices implements ui.Vertices { } } - static _encodePoints(List points) { + static js.JsArray> _encodePoints(List points) { if (points == null) return null; js.JsArray> encodedPoints = diff --git a/lib/web_ui/lib/src/engine/surface/path_metrics.dart b/lib/web_ui/lib/src/engine/surface/path_metrics.dart index 98a95622d3c48..7ad30fd725dc3 100644 --- a/lib/web_ui/lib/src/engine/surface/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/surface/path_metrics.dart @@ -616,7 +616,6 @@ class SurfacePathMetricIterator implements Iterator { SurfacePathMetric _pathMetric; _SurfacePathMeasure _pathMeasure; - bool _firstTime = true; @override SurfacePathMetric get current => _pathMetric; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 03695abc096b3..a3da6d2dbcc00 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -555,9 +555,7 @@ class EngineWindow extends ui.Window { bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { final MethodCall decoded = codec.decodeMethodCall(data); - final Map message = decoded.arguments; double ratio = double.parse(decoded.arguments); - bool result = false; switch(decoded.method) { case 'setDevicePixelRatio': window.debugOverrideDevicePixelRatio(ratio); diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index 9cef2faefb636..b08ef9dafab40 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -1069,8 +1069,6 @@ abstract class Shader { /// There are several types of gradients, represented by the various /// constructors on this class. abstract class Gradient extends Shader { - Gradient._() : super._(); - /// Creates a linear gradient from `from` to `to`. /// /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 8a1ae89fab5a6..a55779537e150 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -866,14 +866,6 @@ class TextBox { this.direction, ); - TextBox._( - this.left, - this.top, - this.right, - this.bottom, - int directionIndex, - ) : direction = TextDirection.values[directionIndex]; - /// The left edge of the text box, irrespective of direction. /// /// To get the leading edge (which may depend on the [direction]), consider [start]. diff --git a/lib/web_ui/test/engine/image/html_image_codec_test.dart b/lib/web_ui/test/engine/image/html_image_codec_test.dart index 3c1f347162d1d..38cd5602d32f4 100644 --- a/lib/web_ui/test/engine/image/html_image_codec_test.dart +++ b/lib/web_ui/test/engine/image/html_image_codec_test.dart @@ -22,7 +22,7 @@ Future main() async { chunkCallback: (int loaded, int total) { buffer.write('$loaded/$total,'); }); - final ui.FrameInfo frameInfo = await codec.getNextFrame(); + await codec.getNextFrame(); expect(buffer.toString(), '0/100,100/100,'); }); }); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart index d6e3ee13e9711..f0d2f15a08a77 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart @@ -4,7 +4,6 @@ // @dart = 2.6 import 'dart:html' as html; -import 'dart:js_util' as js_util; import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart' as engine; diff --git a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart index dcabb01fdbde1..38379087867e2 100644 --- a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart +++ b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart @@ -4,7 +4,6 @@ // @dart = 2.6 import 'dart:html' as html; -import 'dart:typed_data'; import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart'; @@ -63,8 +62,6 @@ void main() async { test('Should calculate tangent on cubic curve', () async { final Path path = Path(); - double p0x = 150; - double p0y = 20; double p1x = 240; double p1y = 120; double p2x = 320; @@ -112,8 +109,6 @@ void main() async { final Path path = Path(); path.moveTo(50, 130); path.lineTo(150, 20); - double p0x = 150; - double p0y = 20; double p1x = 240; double p1y = 120; double p2x = 320; @@ -122,8 +117,6 @@ void main() async { rc.drawPath(path, paint); - final Float32List buffer = Float32List(6); - List points = [p0x, p0y, p1x, p1y, p2x, p2y]; double t0 = 0.2; double t1 = 0.7; @@ -168,8 +161,6 @@ void main() async { final Path path = Path(); path.moveTo(50, 130); path.lineTo(150, 20); - double p0x = 150; - double p0y = 20; double p1x = 40; double p1y = 120; double p2x = 300; @@ -180,8 +171,6 @@ void main() async { rc.drawPath(path, paint); - final Float32List buffer = Float32List(6); - List points = [p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y]; double t0 = 0.2; double t1 = 0.7; diff --git a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart index e46d8c258c277..c8ec261e8b72a 100644 --- a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. // @dart = 2.6 -import 'package:test/test.dart'; import 'package:ui/ui.dart'; import 'package:ui/src/engine.dart'; diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart index 919e5fc97ae61..cf96ab0ef25b9 100644 --- a/lib/web_ui/test/text/measurement_test.dart +++ b/lib/web_ui/test/text/measurement_test.dart @@ -1154,5 +1154,6 @@ EngineLineMetrics line( width: width, lineNumber: lineNumber, left: left, + endIndexWithoutNewlines: -1, ); } From 67e66f7c811839c0e6a8605d1cbbccf4f067b59f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 27 Mar 2020 01:10:15 -0700 Subject: [PATCH 160/386] Disable ShellTest.OnServiceProtocolGetSkSLsWorks. (#17367) Re-enabling tracked in https://github.com/flutter/flutter/issues/53399. --- shell/common/shell_unittests.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 21787017d2d02..e6f9a1f12cbdd 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1132,7 +1132,8 @@ TEST_F(ShellTest, CanDecompressImageFromAsset) { DestroyShell(std::move(shell)); } -TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) { +// TODO(53399): Re-enable once it passes on Fuchsia. +TEST_F(ShellTest, DISABLED_OnServiceProtocolGetSkSLsWorks) { // Create 2 dummpy SkSL cache file IE (base32 encoding of A), II (base32 // encoding of B) with content x and y. fml::ScopedTemporaryDirectory temp_dir; From 6d33ee1a2ca7d404ebd5cb434d8ab859bf8ac0b7 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 27 Mar 2020 02:38:50 -0700 Subject: [PATCH 161/386] Revert "add limited analysis options (#17332)" (#17368) This reverts commit 5389159643fa26342580208b6a9de547ef4de406. --- .cirrus.yml | 10 --- lib/web_ui/analysis_options.yaml | 88 +------------------ lib/web_ui/dev/browser.dart | 2 +- lib/web_ui/dev/firefox_installer_test.dart | 2 +- lib/web_ui/dev/test_platform.dart | 2 +- .../lib/src/engine/compositor/vertices.dart | 2 +- .../lib/src/engine/surface/path_metrics.dart | 1 + lib/web_ui/lib/src/engine/window.dart | 2 + lib/web_ui/lib/src/ui/painting.dart | 2 + lib/web_ui/lib/src/ui/text.dart | 8 ++ .../engine/image/html_image_codec_test.dart | 2 +- .../engine/canvas_context_test.dart | 1 + .../engine/path_metrics_test.dart | 11 +++ .../engine/text_style_golden_test.dart | 1 + lib/web_ui/test/text/measurement_test.dart | 1 - 15 files changed, 32 insertions(+), 103 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 7b954671a7aeb..77d8b81ca4a6f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -126,16 +126,6 @@ task: - name: web_tests-7_last-linux # last Web shard must end with _last << : *WEB_SHARD_TEMPLATE - - name: web_engine_analysis - compile_host_script: | - cd $ENGINE_PATH/src - ./flutter/tools/gn --unoptimized --full-dart-sdk - ninja -C out/host_debug_unopt - script: - - cd $ENGINE_PATH/src/flutter/lib/web_ui - - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/pub get - - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/dartanalyzer --fatal-warnings --fatal-hints dev/ lib/ test/ tool/ - - name: web_engine_integration_test_linux compile_host_script: | cd $ENGINE_PATH/src diff --git a/lib/web_ui/analysis_options.yaml b/lib/web_ui/analysis_options.yaml index 7c46e201fff84..6892f49dc726b 100644 --- a/lib/web_ui/analysis_options.yaml +++ b/lib/web_ui/analysis_options.yaml @@ -1,87 +1 @@ -# This is copy of the root analysis_options.yaml. As we clean up the Web code, -# we'll be uncommenting rules and gradually fix the code. When all rules are -# uncommented, we'll delete this file and simply inherit the root options. - -analyzer: - # TODO(uncomment) strong-mode: - # TODO(uncomment) implicit-casts: false - # TODO(uncomment) implicit-dynamic: false - errors: - missing_required_param: warning - missing_return: warning - native_function_body_in_non_sdk_code: ignore - todo: ignore - -linter: - rules: - - always_declare_return_types - # TODO(uncomment) - always_put_control_body_on_new_line - # TODO(uncomment) - always_specify_types - # TODO(uncomment) - annotate_overrides - # TODO(uncomment) - avoid_classes_with_only_static_members - # TODO(uncomment) - avoid_empty_else - # TODO(uncomment) - avoid_function_literals_in_foreach_calls - # TODO(uncomment) - avoid_init_to_null - # TODO(uncomment) - avoid_null_checks_in_equality_operators - # TODO(uncomment) - avoid_relative_lib_imports - # TODO(uncomment) - avoid_renaming_method_parameters - # TODO(uncomment) - avoid_return_types_on_setters - # TODO(uncomment) - avoid_slow_async_io - # TODO(uncomment) - await_only_futures - # TODO(uncomment) - camel_case_types - # TODO(uncomment) - cancel_subscriptions - # TODO(uncomment) - control_flow_in_finally - # TODO(uncomment) - directives_ordering - # TODO(uncomment) - empty_catches - # TODO(uncomment) - empty_constructor_bodies - # TODO(uncomment) - empty_statements - # TODO(uncomment) - hash_and_equals - # TODO(uncomment) - implementation_imports - # TODO(uncomment) - iterable_contains_unrelated_type - # TODO(uncomment) - library_names - # TODO(uncomment) - library_prefixes - # TODO(uncomment) - list_remove_unrelated_type - # TODO(uncomment) - no_adjacent_strings_in_list - # TODO(uncomment) - no_duplicate_case_values - # TODO(uncomment) - non_constant_identifier_names - # TODO(uncomment) - overridden_fields - # TODO(uncomment) - package_api_docs - # TODO(uncomment) - package_names - # TODO(uncomment) - package_prefixed_library_names - # TODO(uncomment) - prefer_adjacent_string_concatenation - # TODO(uncomment) - prefer_asserts_in_initializer_lists - # TODO(uncomment) - prefer_collection_literals - # TODO(uncomment) - prefer_conditional_assignment - # TODO(uncomment) - prefer_const_constructors - # TODO(uncomment) - prefer_const_constructors_in_immutables - # TODO(uncomment) - prefer_const_declarations - # TODO(uncomment) - prefer_const_literals_to_create_immutables - # TODO(uncomment) - prefer_contains - # TODO(uncomment) - prefer_equal_for_default_values - # TODO(uncomment) - prefer_final_locals - # TODO(uncomment) - prefer_foreach - # TODO(uncomment) - prefer_generic_function_type_aliases - # TODO(uncomment) - prefer_initializing_formals - # TODO(uncomment) - prefer_is_empty - # TODO(uncomment) - prefer_is_not_empty - # TODO(uncomment) - prefer_single_quotes - # TODO(uncomment) - prefer_typing_uninitialized_variables - # TODO(uncomment) - public_member_api_docs - # TODO(uncomment) - recursive_getters - # TODO(uncomment) - slash_for_doc_comments - # TODO(uncomment) - sort_unnamed_constructors_first - # TODO(uncomment) - test_types_in_equals - # TODO(uncomment) - throw_in_finally - # TODO(uncomment) - type_init_formals - # TODO(uncomment) - unnecessary_brace_in_string_interps - # TODO(uncomment) - unnecessary_const - # TODO(uncomment) - unnecessary_getters_setters - # TODO(uncomment) - unnecessary_new - # TODO(uncomment) - unnecessary_null_aware_assignments - # TODO(uncomment) - unnecessary_null_in_if_null_operators - # TODO(uncomment) - unnecessary_overrides - # TODO(uncomment) - unnecessary_parenthesis - # TODO(uncomment) - unnecessary_this - # TODO(uncomment) - unrelated_type_equality_checks - # TODO(uncomment) - use_rethrow_when_possible - # TODO(uncomment) - valid_regexps +# Intentionally kept empty. \ No newline at end of file diff --git a/lib/web_ui/dev/browser.dart b/lib/web_ui/dev/browser.dart index 989609cd8a5bf..8c4c736e99aab 100644 --- a/lib/web_ui/dev/browser.dart +++ b/lib/web_ui/dev/browser.dart @@ -72,7 +72,7 @@ abstract class Browser { _processCompleter.complete(process); var output = Uint8Buffer(); - void drainOutput(Stream> stream) { + drainOutput(Stream> stream) { try { _ioSubscriptions .add(stream.listen(output.addAll, cancelOnError: true)); diff --git a/lib/web_ui/dev/firefox_installer_test.dart b/lib/web_ui/dev/firefox_installer_test.dart index fc18277f53b14..2d687d7be1c6b 100644 --- a/lib/web_ui/dev/firefox_installer_test.dart +++ b/lib/web_ui/dev/firefox_installer_test.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.6 @TestOn('vm && linux') +// @dart = 2.6 import 'dart:io' as io; import 'package:path/path.dart' as path; diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 1e5aa3c79b7b8..6b4663d5d4a91 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -744,7 +744,7 @@ class BrowserManager { var suiteID = _suiteID++; RunnerSuiteController controller; - void closeIframe() { + closeIframe() { if (_closed) return; _controllers.remove(controller); _channel.sink.add({'command': 'closeSuite', 'id': suiteID}); diff --git a/lib/web_ui/lib/src/engine/compositor/vertices.dart b/lib/web_ui/lib/src/engine/compositor/vertices.dart index b9d43ba6d262b..67164c498411c 100644 --- a/lib/web_ui/lib/src/engine/compositor/vertices.dart +++ b/lib/web_ui/lib/src/engine/compositor/vertices.dart @@ -116,7 +116,7 @@ class SkVertices implements ui.Vertices { } } - static js.JsArray> _encodePoints(List points) { + static _encodePoints(List points) { if (points == null) return null; js.JsArray> encodedPoints = diff --git a/lib/web_ui/lib/src/engine/surface/path_metrics.dart b/lib/web_ui/lib/src/engine/surface/path_metrics.dart index 7ad30fd725dc3..98a95622d3c48 100644 --- a/lib/web_ui/lib/src/engine/surface/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/surface/path_metrics.dart @@ -616,6 +616,7 @@ class SurfacePathMetricIterator implements Iterator { SurfacePathMetric _pathMetric; _SurfacePathMeasure _pathMeasure; + bool _firstTime = true; @override SurfacePathMetric get current => _pathMetric; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index a3da6d2dbcc00..03695abc096b3 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -555,7 +555,9 @@ class EngineWindow extends ui.Window { bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { final MethodCall decoded = codec.decodeMethodCall(data); + final Map message = decoded.arguments; double ratio = double.parse(decoded.arguments); + bool result = false; switch(decoded.method) { case 'setDevicePixelRatio': window.debugOverrideDevicePixelRatio(ratio); diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index b08ef9dafab40..9cef2faefb636 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -1069,6 +1069,8 @@ abstract class Shader { /// There are several types of gradients, represented by the various /// constructors on this class. abstract class Gradient extends Shader { + Gradient._() : super._(); + /// Creates a linear gradient from `from` to `to`. /// /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index a55779537e150..8a1ae89fab5a6 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -866,6 +866,14 @@ class TextBox { this.direction, ); + TextBox._( + this.left, + this.top, + this.right, + this.bottom, + int directionIndex, + ) : direction = TextDirection.values[directionIndex]; + /// The left edge of the text box, irrespective of direction. /// /// To get the leading edge (which may depend on the [direction]), consider [start]. diff --git a/lib/web_ui/test/engine/image/html_image_codec_test.dart b/lib/web_ui/test/engine/image/html_image_codec_test.dart index 38cd5602d32f4..3c1f347162d1d 100644 --- a/lib/web_ui/test/engine/image/html_image_codec_test.dart +++ b/lib/web_ui/test/engine/image/html_image_codec_test.dart @@ -22,7 +22,7 @@ Future main() async { chunkCallback: (int loaded, int total) { buffer.write('$loaded/$total,'); }); - await codec.getNextFrame(); + final ui.FrameInfo frameInfo = await codec.getNextFrame(); expect(buffer.toString(), '0/100,100/100,'); }); }); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart index f0d2f15a08a77..d6e3ee13e9711 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart @@ -4,6 +4,7 @@ // @dart = 2.6 import 'dart:html' as html; +import 'dart:js_util' as js_util; import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart' as engine; diff --git a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart index 38379087867e2..dcabb01fdbde1 100644 --- a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart +++ b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart @@ -4,6 +4,7 @@ // @dart = 2.6 import 'dart:html' as html; +import 'dart:typed_data'; import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart'; @@ -62,6 +63,8 @@ void main() async { test('Should calculate tangent on cubic curve', () async { final Path path = Path(); + double p0x = 150; + double p0y = 20; double p1x = 240; double p1y = 120; double p2x = 320; @@ -109,6 +112,8 @@ void main() async { final Path path = Path(); path.moveTo(50, 130); path.lineTo(150, 20); + double p0x = 150; + double p0y = 20; double p1x = 240; double p1y = 120; double p2x = 320; @@ -117,6 +122,8 @@ void main() async { rc.drawPath(path, paint); + final Float32List buffer = Float32List(6); + List points = [p0x, p0y, p1x, p1y, p2x, p2y]; double t0 = 0.2; double t1 = 0.7; @@ -161,6 +168,8 @@ void main() async { final Path path = Path(); path.moveTo(50, 130); path.lineTo(150, 20); + double p0x = 150; + double p0y = 20; double p1x = 40; double p1y = 120; double p2x = 300; @@ -171,6 +180,8 @@ void main() async { rc.drawPath(path, paint); + final Float32List buffer = Float32List(6); + List points = [p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y]; double t0 = 0.2; double t1 = 0.7; diff --git a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart index c8ec261e8b72a..e46d8c258c277 100644 --- a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. // @dart = 2.6 +import 'package:test/test.dart'; import 'package:ui/ui.dart'; import 'package:ui/src/engine.dart'; diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart index cf96ab0ef25b9..919e5fc97ae61 100644 --- a/lib/web_ui/test/text/measurement_test.dart +++ b/lib/web_ui/test/text/measurement_test.dart @@ -1154,6 +1154,5 @@ EngineLineMetrics line( width: width, lineNumber: lineNumber, left: left, - endIndexWithoutNewlines: -1, ); } From 82c7ab09a4d8fe50ff6a1d78f23260a97d29a835 Mon Sep 17 00:00:00 2001 From: wqyfavor Date: Fri, 27 Mar 2020 17:40:56 +0800 Subject: [PATCH 162/386] Fix problem that using multi-engines, sometimes OpenGL would crash be cause of invalid EAGLContext (#17366) --- shell/platform/darwin/ios/ios_render_target_gl.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/ios_render_target_gl.mm b/shell/platform/darwin/ios/ios_render_target_gl.mm index 34b57a787d843..7f58a7d7fa24b 100644 --- a/shell/platform/darwin/ios/ios_render_target_gl.mm +++ b/shell/platform/darwin/ios/ios_render_target_gl.mm @@ -70,7 +70,11 @@ glDeleteRenderbuffers(1, &colorbuffer_); FML_DCHECK(glGetError() == GL_NO_ERROR); - [EAGLContext setCurrentContext:context]; + if (context == context_.get()) { + [EAGLContext setCurrentContext:nil]; + } else { + [EAGLContext setCurrentContext:context]; + } } // |IOSRenderTarget| From 020d7c5c80eee802f36ef86d7aebccb6a7ff49aa Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 27 Mar 2020 04:57:07 -0700 Subject: [PATCH 163/386] Locale: empty string equates to null (#17356) --- lib/ui/window.dart | 8 +++++--- testing/dart/locale_test.dart | 12 ++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index c0b6bb9fe861c..1c9399b670eaa 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -475,12 +475,14 @@ class Locale { return true; return other is Locale && other.languageCode == languageCode - && other.scriptCode == scriptCode - && other.countryCode == countryCode; + && other.scriptCode == scriptCode // scriptCode cannot be '' + && (other.countryCode == countryCode // Treat '' as equal to null. + || other.countryCode != null && other.countryCode.isEmpty && countryCode == null + || countryCode != null && countryCode.isEmpty && other.countryCode == null); } @override - int get hashCode => hashValues(languageCode, scriptCode, countryCode); + int get hashCode => hashValues(languageCode, scriptCode, countryCode == '' ? null : countryCode); static Locale _cachedLocale; static String _cachedLocaleString; diff --git a/testing/dart/locale_test.dart b/testing/dart/locale_test.dart index 5bebd5106be6d..64c16db78688b 100644 --- a/testing/dart/locale_test.dart +++ b/testing/dart/locale_test.dart @@ -13,8 +13,6 @@ void main() { expect(const Locale('en').toLanguageTag(), 'en'); expect(const Locale('en'), const Locale('en', $null)); expect(const Locale('en').hashCode, const Locale('en', $null).hashCode); - expect(const Locale('en'), isNot(const Locale('en', ''))); - expect(const Locale('en').hashCode, isNot(const Locale('en', '').hashCode)); expect(const Locale('en', 'US').toLanguageTag(), 'en-US'); expect(const Locale('en', 'US').toString(), 'en_US'); expect(const Locale('iw').toLanguageTag(), 'he'); @@ -52,6 +50,16 @@ void main() { isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn'))); expect(const Locale.fromSubtags(languageCode: 'en').hashCode, isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn').hashCode)); + + expect(const Locale('en', ''), const Locale('en')); + expect(const Locale('en'), const Locale('en', '')); + expect(const Locale('en'), const Locale('en')); + expect(const Locale('en', ''), const Locale('en', '')); + + expect(const Locale('en', ''), isNot(const Locale('en', 'GB'))); + expect(const Locale('en'), isNot(const Locale('en', 'GB'))); + expect(const Locale('en', 'GB'), isNot(const Locale('en', ''))); + expect(const Locale('en', 'GB'), isNot(const Locale('en'))); }); test('Locale toString does not include separator for \'\'', () { From 2bb41774e985f26ec5ce84ba5aa2959868d7ccc3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 27 Mar 2020 11:22:22 -0400 Subject: [PATCH 164/386] Roll src/third_party/skia 265de3a93c6e..385de38b272f (22 commits) (#17369) https://skia.googlesource.com/skia.git/+log/265de3a93c6e..385de38b272f git log 265de3a93c6e..385de38b272f --date=short --first-parent --format='%ad %ae %s' 2020-03-27 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update Go Deps 2020-03-27 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 6719f54ac7ce..f2bee3043a5e (6 commits) 2020-03-27 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 77090261dabc..02e15b249b12 (8 commits) 2020-03-27 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 16171704b6dc..2cb42d2f8146 (394 commits) 2020-03-26 mtklein@google.com implement matrix 0/1 todo 2020-03-26 brianosman@google.com Disable Processor tests on TecnoSpark3Pro 2020-03-26 reed@google.com handle HSL case in HSLMatrix for skvm 2020-03-26 reed@google.com add bench for highcontrast-colorfilter 2020-03-26 fmalita@chromium.org [skottie] Cleanup: relocate Animator base class out of SkSG 2020-03-26 michaelludwig@google.com Correctly initialize LayerIter's cached device origin. 2020-03-26 jvanverth@google.com Add GrD3DRenderTarget and GrD3DTextureRenderTarget. 2020-03-26 jlavrova@google.com Respect grapheme clusters when wrapping text 2020-03-26 csmartdalton@google.com Add glDraw*BaseInstance functions to GrGLInterface 2020-03-26 bungeman@google.com Include system headers last. 2020-03-26 jvanverth@google.com Rename GrD3DTextureInfo to GrD3DTextureResourceInfo. 2020-03-26 bungeman@google.com Begin fixing GrShaderVar. 2020-03-26 csmartdalton@google.com Rename CPU tessellation to "triangulation" 2020-03-26 brianosman@google.com Reland "Switch sRGB color filter to use GrColorSpaceXformEffect" 2020-03-26 brianosman@google.com Update comment about Mali G series 2020-03-26 reed@google.com onProgram for HighContrastColorFilter 2020-03-26 brianosman@google.com Add workaround flag for color space transform math on Mali G GPUs 2020-03-26 nifong@google.com Reland "Switch to using a Float32Array (bound as value array) for color." Created with: gclient setdep -r src/third_party/skia@385de38b272f If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/DEPS b/DEPS index 1cdade9a8b0a4..03098f531e4c4 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '265de3a93c6ec3f21e1b1e01e89e4a4c712f9d6c', + 'skia_revision': '385de38b272f6355fb0911ece994ffa71e7a35c2', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 9fb03cb11f712..015bfe293d6c8 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 107380be15d2ea8a6af16f32e612af59 +Signature: eea4d9938ea13509a3f4156d3aac0a12 UNUSED LICENSES: @@ -2452,10 +2452,10 @@ FILE: ../../../third_party/skia/src/gpu/GrResourceProvider.cpp FILE: ../../../third_party/skia/src/gpu/GrResourceProvider.h FILE: ../../../third_party/skia/src/gpu/GrSamplerState.h FILE: ../../../third_party/skia/src/gpu/GrSimpleMesh.h -FILE: ../../../third_party/skia/src/gpu/GrTessellator.cpp -FILE: ../../../third_party/skia/src/gpu/GrTessellator.h FILE: ../../../third_party/skia/src/gpu/GrTestUtils.cpp FILE: ../../../third_party/skia/src/gpu/GrTestUtils.h +FILE: ../../../third_party/skia/src/gpu/GrTriangulator.cpp +FILE: ../../../third_party/skia/src/gpu/GrTriangulator.h FILE: ../../../third_party/skia/src/gpu/GrXferProcessor.cpp FILE: ../../../third_party/skia/src/gpu/GrYUVProvider.cpp FILE: ../../../third_party/skia/src/gpu/GrYUVProvider.h @@ -2503,8 +2503,8 @@ FILE: ../../../third_party/skia/src/gpu/ops/GrMeshDrawOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrOp.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrOp.h FILE: ../../../third_party/skia/src/gpu/ops/GrStencilPathOp.h -FILE: ../../../third_party/skia/src/gpu/ops/GrTessellatingPathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/ops/GrTessellatingPathRenderer.h +FILE: ../../../third_party/skia/src/gpu/ops/GrTriangulatingPathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/ops/GrTriangulatingPathRenderer.h FILE: ../../../third_party/skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp FILE: ../../../third_party/skia/src/gpu/text/GrDistanceFieldAdjustTable.h FILE: ../../../third_party/skia/src/gpu/text/GrStrikeCache.cpp @@ -2878,7 +2878,7 @@ FILE: ../../../third_party/skia/include/effects/SkRuntimeEffect.h FILE: ../../../third_party/skia/include/gpu/gl/GrGLAssembleHelpers.h FILE: ../../../third_party/skia/include/private/GrGLTypesPriv.h FILE: ../../../third_party/skia/include/private/SkThreadAnnotations.h -FILE: ../../../third_party/skia/modules/canvaskit/WasmAliases.h +FILE: ../../../third_party/skia/modules/canvaskit/WasmCommon.h FILE: ../../../third_party/skia/modules/canvaskit/paragraph_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/particles_bindings.cpp FILE: ../../../third_party/skia/modules/canvaskit/skottie_bindings.cpp @@ -3913,13 +3913,17 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DRenderTarget.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DRenderTarget.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceState.h -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DSurfaceResource.cpp -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DSurfaceResource.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTexture.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTextureRenderTarget.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTextureRenderTarget.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTextureResource.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTextureResource.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTypesPriv.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.h From 662e7804f47de8040199dc2a0d9562b5cac6253b Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Fri, 27 Mar 2020 13:28:03 -0400 Subject: [PATCH 165/386] Guard SkCanvas bitmap virtuals for deprecation and removal (#17371) --- shell/common/canvas_spy.cc | 2 ++ shell/common/canvas_spy.h | 2 ++ testing/mock_canvas.cc | 4 ++++ testing/mock_canvas.h | 4 ++++ 4 files changed, 12 insertions(+) diff --git a/shell/common/canvas_spy.cc b/shell/common/canvas_spy.cc index d31a86f18d158..2cdb71f7eadaf 100644 --- a/shell/common/canvas_spy.cc +++ b/shell/common/canvas_spy.cc @@ -124,6 +124,7 @@ void DidDrawCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { MarkDrawIfNonTransparentPaint(paint); } +#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) void DidDrawCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, @@ -138,6 +139,7 @@ void DidDrawCanvas::onDrawBitmapRect(const SkBitmap& bitmap, SrcRectConstraint constraint) { did_draw_ = true; } +#endif void DidDrawCanvas::onDrawImage(const SkImage* image, SkScalar left, diff --git a/shell/common/canvas_spy.h b/shell/common/canvas_spy.h index 0fc466bb05938..4aab238eecf48 100644 --- a/shell/common/canvas_spy.h +++ b/shell/common/canvas_spy.h @@ -127,6 +127,7 @@ class DidDrawCanvas final : public SkCanvasVirtualEnforcer { // |SkCanvasVirtualEnforcer| void onDrawPath(const SkPath&, const SkPaint&) override; +#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) // |SkCanvasVirtualEnforcer| void onDrawBitmap(const SkBitmap&, SkScalar left, @@ -139,6 +140,7 @@ class DidDrawCanvas final : public SkCanvasVirtualEnforcer { const SkRect& dst, const SkPaint*, SrcRectConstraint) override; +#endif // |SkCanvasVirtualEnforcer| void onDrawImage(const SkImage*, diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc index c1c766b0b610f..88ab5f9ed196d 100644 --- a/testing/mock_canvas.cc +++ b/testing/mock_canvas.cc @@ -225,12 +225,14 @@ void MockCanvas::onDrawRRect(const SkRRect&, const SkPaint&) { FML_DCHECK(false); } +#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) void MockCanvas::onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) { FML_DCHECK(false); } +#endif void MockCanvas::onDrawImage(const SkImage*, SkScalar, @@ -239,6 +241,7 @@ void MockCanvas::onDrawImage(const SkImage*, FML_DCHECK(false); } +#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) void MockCanvas::onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, @@ -246,6 +249,7 @@ void MockCanvas::onDrawBitmapRect(const SkBitmap&, SrcRectConstraint) { FML_DCHECK(false); } +#endif void MockCanvas::onDrawImageRect(const SkImage*, const SkRect*, diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h index 17ef16bfa660e..e7438db5fba86 100644 --- a/testing/mock_canvas.h +++ b/testing/mock_canvas.h @@ -195,11 +195,13 @@ class MockCanvas : public SkCanvasVirtualEnforcer { bool, const SkPaint&) override; void onDrawRRect(const SkRRect&, const SkPaint&) override; +#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*, SrcRectConstraint) override; +#endif void onDrawImage(const SkImage* image, SkScalar x, SkScalar y, @@ -213,10 +215,12 @@ class MockCanvas : public SkCanvasVirtualEnforcer { const SkIRect&, const SkRect&, const SkPaint*) override; +#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) void onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) override; +#endif void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, From b2a7470748e3903593754a41b0df3293dfddf0fd Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 27 Mar 2020 11:06:10 -0700 Subject: [PATCH 166/386] Reland "add limited analysis options (#17332)" (#17374) * Reland "add limited analysis options (#17332)" This time I'm starting without Cirrus. Will add Cirrus serpartely from code changes. This reverts commit 6d33ee1a2ca7d404ebd5cb434d8ab859bf8ac0b7. * disable Cirrus analysis check --- .cirrus.yml | 10 +++ lib/web_ui/analysis_options.yaml | 88 ++++++++++++++++++- lib/web_ui/dev/browser.dart | 2 +- lib/web_ui/dev/firefox_installer_test.dart | 2 +- lib/web_ui/dev/test_platform.dart | 2 +- .../lib/src/engine/compositor/vertices.dart | 2 +- .../lib/src/engine/surface/path_metrics.dart | 1 - lib/web_ui/lib/src/engine/window.dart | 2 - lib/web_ui/lib/src/ui/painting.dart | 2 - lib/web_ui/lib/src/ui/text.dart | 8 -- .../engine/image/html_image_codec_test.dart | 2 +- lib/web_ui/test/engine/profiler_test.dart | 2 +- .../engine/canvas_context_test.dart | 1 - .../engine/path_metrics_test.dart | 11 --- .../engine/text_style_golden_test.dart | 1 - lib/web_ui/test/text/measurement_test.dart | 1 + 16 files changed, 104 insertions(+), 33 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 77d8b81ca4a6f..cfd659e488540 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -126,6 +126,16 @@ task: - name: web_tests-7_last-linux # last Web shard must end with _last << : *WEB_SHARD_TEMPLATE + # - name: web_engine_analysis + # compile_host_script: | + # cd $ENGINE_PATH/src + # ./flutter/tools/gn --unoptimized --full-dart-sdk + # ninja -C out/host_debug_unopt + # script: + # - cd $ENGINE_PATH/src/flutter/lib/web_ui + # - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/pub get + # - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/dartanalyzer --fatal-warnings --fatal-hints dev/ lib/ test/ tool/ + - name: web_engine_integration_test_linux compile_host_script: | cd $ENGINE_PATH/src diff --git a/lib/web_ui/analysis_options.yaml b/lib/web_ui/analysis_options.yaml index 6892f49dc726b..7c46e201fff84 100644 --- a/lib/web_ui/analysis_options.yaml +++ b/lib/web_ui/analysis_options.yaml @@ -1 +1,87 @@ -# Intentionally kept empty. \ No newline at end of file +# This is copy of the root analysis_options.yaml. As we clean up the Web code, +# we'll be uncommenting rules and gradually fix the code. When all rules are +# uncommented, we'll delete this file and simply inherit the root options. + +analyzer: + # TODO(uncomment) strong-mode: + # TODO(uncomment) implicit-casts: false + # TODO(uncomment) implicit-dynamic: false + errors: + missing_required_param: warning + missing_return: warning + native_function_body_in_non_sdk_code: ignore + todo: ignore + +linter: + rules: + - always_declare_return_types + # TODO(uncomment) - always_put_control_body_on_new_line + # TODO(uncomment) - always_specify_types + # TODO(uncomment) - annotate_overrides + # TODO(uncomment) - avoid_classes_with_only_static_members + # TODO(uncomment) - avoid_empty_else + # TODO(uncomment) - avoid_function_literals_in_foreach_calls + # TODO(uncomment) - avoid_init_to_null + # TODO(uncomment) - avoid_null_checks_in_equality_operators + # TODO(uncomment) - avoid_relative_lib_imports + # TODO(uncomment) - avoid_renaming_method_parameters + # TODO(uncomment) - avoid_return_types_on_setters + # TODO(uncomment) - avoid_slow_async_io + # TODO(uncomment) - await_only_futures + # TODO(uncomment) - camel_case_types + # TODO(uncomment) - cancel_subscriptions + # TODO(uncomment) - control_flow_in_finally + # TODO(uncomment) - directives_ordering + # TODO(uncomment) - empty_catches + # TODO(uncomment) - empty_constructor_bodies + # TODO(uncomment) - empty_statements + # TODO(uncomment) - hash_and_equals + # TODO(uncomment) - implementation_imports + # TODO(uncomment) - iterable_contains_unrelated_type + # TODO(uncomment) - library_names + # TODO(uncomment) - library_prefixes + # TODO(uncomment) - list_remove_unrelated_type + # TODO(uncomment) - no_adjacent_strings_in_list + # TODO(uncomment) - no_duplicate_case_values + # TODO(uncomment) - non_constant_identifier_names + # TODO(uncomment) - overridden_fields + # TODO(uncomment) - package_api_docs + # TODO(uncomment) - package_names + # TODO(uncomment) - package_prefixed_library_names + # TODO(uncomment) - prefer_adjacent_string_concatenation + # TODO(uncomment) - prefer_asserts_in_initializer_lists + # TODO(uncomment) - prefer_collection_literals + # TODO(uncomment) - prefer_conditional_assignment + # TODO(uncomment) - prefer_const_constructors + # TODO(uncomment) - prefer_const_constructors_in_immutables + # TODO(uncomment) - prefer_const_declarations + # TODO(uncomment) - prefer_const_literals_to_create_immutables + # TODO(uncomment) - prefer_contains + # TODO(uncomment) - prefer_equal_for_default_values + # TODO(uncomment) - prefer_final_locals + # TODO(uncomment) - prefer_foreach + # TODO(uncomment) - prefer_generic_function_type_aliases + # TODO(uncomment) - prefer_initializing_formals + # TODO(uncomment) - prefer_is_empty + # TODO(uncomment) - prefer_is_not_empty + # TODO(uncomment) - prefer_single_quotes + # TODO(uncomment) - prefer_typing_uninitialized_variables + # TODO(uncomment) - public_member_api_docs + # TODO(uncomment) - recursive_getters + # TODO(uncomment) - slash_for_doc_comments + # TODO(uncomment) - sort_unnamed_constructors_first + # TODO(uncomment) - test_types_in_equals + # TODO(uncomment) - throw_in_finally + # TODO(uncomment) - type_init_formals + # TODO(uncomment) - unnecessary_brace_in_string_interps + # TODO(uncomment) - unnecessary_const + # TODO(uncomment) - unnecessary_getters_setters + # TODO(uncomment) - unnecessary_new + # TODO(uncomment) - unnecessary_null_aware_assignments + # TODO(uncomment) - unnecessary_null_in_if_null_operators + # TODO(uncomment) - unnecessary_overrides + # TODO(uncomment) - unnecessary_parenthesis + # TODO(uncomment) - unnecessary_this + # TODO(uncomment) - unrelated_type_equality_checks + # TODO(uncomment) - use_rethrow_when_possible + # TODO(uncomment) - valid_regexps diff --git a/lib/web_ui/dev/browser.dart b/lib/web_ui/dev/browser.dart index 8c4c736e99aab..989609cd8a5bf 100644 --- a/lib/web_ui/dev/browser.dart +++ b/lib/web_ui/dev/browser.dart @@ -72,7 +72,7 @@ abstract class Browser { _processCompleter.complete(process); var output = Uint8Buffer(); - drainOutput(Stream> stream) { + void drainOutput(Stream> stream) { try { _ioSubscriptions .add(stream.listen(output.addAll, cancelOnError: true)); diff --git a/lib/web_ui/dev/firefox_installer_test.dart b/lib/web_ui/dev/firefox_installer_test.dart index 2d687d7be1c6b..fc18277f53b14 100644 --- a/lib/web_ui/dev/firefox_installer_test.dart +++ b/lib/web_ui/dev/firefox_installer_test.dart @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// @dart = 2.6 @TestOn('vm && linux') -// @dart = 2.6 import 'dart:io' as io; import 'package:path/path.dart' as path; diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 6b4663d5d4a91..1e5aa3c79b7b8 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -744,7 +744,7 @@ class BrowserManager { var suiteID = _suiteID++; RunnerSuiteController controller; - closeIframe() { + void closeIframe() { if (_closed) return; _controllers.remove(controller); _channel.sink.add({'command': 'closeSuite', 'id': suiteID}); diff --git a/lib/web_ui/lib/src/engine/compositor/vertices.dart b/lib/web_ui/lib/src/engine/compositor/vertices.dart index 67164c498411c..b9d43ba6d262b 100644 --- a/lib/web_ui/lib/src/engine/compositor/vertices.dart +++ b/lib/web_ui/lib/src/engine/compositor/vertices.dart @@ -116,7 +116,7 @@ class SkVertices implements ui.Vertices { } } - static _encodePoints(List points) { + static js.JsArray> _encodePoints(List points) { if (points == null) return null; js.JsArray> encodedPoints = diff --git a/lib/web_ui/lib/src/engine/surface/path_metrics.dart b/lib/web_ui/lib/src/engine/surface/path_metrics.dart index 98a95622d3c48..7ad30fd725dc3 100644 --- a/lib/web_ui/lib/src/engine/surface/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/surface/path_metrics.dart @@ -616,7 +616,6 @@ class SurfacePathMetricIterator implements Iterator { SurfacePathMetric _pathMetric; _SurfacePathMeasure _pathMeasure; - bool _firstTime = true; @override SurfacePathMetric get current => _pathMetric; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 03695abc096b3..a3da6d2dbcc00 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -555,9 +555,7 @@ class EngineWindow extends ui.Window { bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { final MethodCall decoded = codec.decodeMethodCall(data); - final Map message = decoded.arguments; double ratio = double.parse(decoded.arguments); - bool result = false; switch(decoded.method) { case 'setDevicePixelRatio': window.debugOverrideDevicePixelRatio(ratio); diff --git a/lib/web_ui/lib/src/ui/painting.dart b/lib/web_ui/lib/src/ui/painting.dart index 9cef2faefb636..b08ef9dafab40 100644 --- a/lib/web_ui/lib/src/ui/painting.dart +++ b/lib/web_ui/lib/src/ui/painting.dart @@ -1069,8 +1069,6 @@ abstract class Shader { /// There are several types of gradients, represented by the various /// constructors on this class. abstract class Gradient extends Shader { - Gradient._() : super._(); - /// Creates a linear gradient from `from` to `to`. /// /// If `colorStops` is provided, `colorStops[i]` is a number from 0.0 to 1.0 diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index 8a1ae89fab5a6..a55779537e150 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -866,14 +866,6 @@ class TextBox { this.direction, ); - TextBox._( - this.left, - this.top, - this.right, - this.bottom, - int directionIndex, - ) : direction = TextDirection.values[directionIndex]; - /// The left edge of the text box, irrespective of direction. /// /// To get the leading edge (which may depend on the [direction]), consider [start]. diff --git a/lib/web_ui/test/engine/image/html_image_codec_test.dart b/lib/web_ui/test/engine/image/html_image_codec_test.dart index 3c1f347162d1d..38cd5602d32f4 100644 --- a/lib/web_ui/test/engine/image/html_image_codec_test.dart +++ b/lib/web_ui/test/engine/image/html_image_codec_test.dart @@ -22,7 +22,7 @@ Future main() async { chunkCallback: (int loaded, int total) { buffer.write('$loaded/$total,'); }); - final ui.FrameInfo frameInfo = await codec.getNextFrame(); + await codec.getNextFrame(); expect(buffer.toString(), '0/100,100/100,'); }); }); diff --git a/lib/web_ui/test/engine/profiler_test.dart b/lib/web_ui/test/engine/profiler_test.dart index 58f12d17cf758..04d580d2fa969 100644 --- a/lib/web_ui/test/engine/profiler_test.dart +++ b/lib/web_ui/test/engine/profiler_test.dart @@ -78,7 +78,7 @@ class BenchmarkDatapoint { int get hashCode => hashValues(name, value); @override - operator ==(dynamic other) { + bool operator ==(dynamic other) { if (identical(this, other)) { return true; } diff --git a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart index d6e3ee13e9711..f0d2f15a08a77 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart @@ -4,7 +4,6 @@ // @dart = 2.6 import 'dart:html' as html; -import 'dart:js_util' as js_util; import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart' as engine; diff --git a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart index dcabb01fdbde1..38379087867e2 100644 --- a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart +++ b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart @@ -4,7 +4,6 @@ // @dart = 2.6 import 'dart:html' as html; -import 'dart:typed_data'; import 'package:ui/ui.dart' hide TextStyle; import 'package:ui/src/engine.dart'; @@ -63,8 +62,6 @@ void main() async { test('Should calculate tangent on cubic curve', () async { final Path path = Path(); - double p0x = 150; - double p0y = 20; double p1x = 240; double p1y = 120; double p2x = 320; @@ -112,8 +109,6 @@ void main() async { final Path path = Path(); path.moveTo(50, 130); path.lineTo(150, 20); - double p0x = 150; - double p0y = 20; double p1x = 240; double p1y = 120; double p2x = 320; @@ -122,8 +117,6 @@ void main() async { rc.drawPath(path, paint); - final Float32List buffer = Float32List(6); - List points = [p0x, p0y, p1x, p1y, p2x, p2y]; double t0 = 0.2; double t1 = 0.7; @@ -168,8 +161,6 @@ void main() async { final Path path = Path(); path.moveTo(50, 130); path.lineTo(150, 20); - double p0x = 150; - double p0y = 20; double p1x = 40; double p1y = 120; double p2x = 300; @@ -180,8 +171,6 @@ void main() async { rc.drawPath(path, paint); - final Float32List buffer = Float32List(6); - List points = [p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y]; double t0 = 0.2; double t1 = 0.7; diff --git a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart index e46d8c258c277..c8ec261e8b72a 100644 --- a/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/text_style_golden_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. // @dart = 2.6 -import 'package:test/test.dart'; import 'package:ui/ui.dart'; import 'package:ui/src/engine.dart'; diff --git a/lib/web_ui/test/text/measurement_test.dart b/lib/web_ui/test/text/measurement_test.dart index 919e5fc97ae61..cf96ab0ef25b9 100644 --- a/lib/web_ui/test/text/measurement_test.dart +++ b/lib/web_ui/test/text/measurement_test.dart @@ -1154,5 +1154,6 @@ EngineLineMetrics line( width: width, lineNumber: lineNumber, left: left, + endIndexWithoutNewlines: -1, ); } From dfb7d930a813e9d141da8b895fc1205851e1efdb Mon Sep 17 00:00:00 2001 From: Yegor Date: Fri, 27 Mar 2020 11:46:02 -0700 Subject: [PATCH 167/386] enable Dart analysis for Web on Cirrus (#17377) --- .cirrus.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index cfd659e488540..7b954671a7aeb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -126,15 +126,15 @@ task: - name: web_tests-7_last-linux # last Web shard must end with _last << : *WEB_SHARD_TEMPLATE - # - name: web_engine_analysis - # compile_host_script: | - # cd $ENGINE_PATH/src - # ./flutter/tools/gn --unoptimized --full-dart-sdk - # ninja -C out/host_debug_unopt - # script: - # - cd $ENGINE_PATH/src/flutter/lib/web_ui - # - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/pub get - # - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/dartanalyzer --fatal-warnings --fatal-hints dev/ lib/ test/ tool/ + - name: web_engine_analysis + compile_host_script: | + cd $ENGINE_PATH/src + ./flutter/tools/gn --unoptimized --full-dart-sdk + ninja -C out/host_debug_unopt + script: + - cd $ENGINE_PATH/src/flutter/lib/web_ui + - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/pub get + - $ENGINE_PATH/src/out/host_debug_unopt/dart-sdk/bin/dartanalyzer --fatal-warnings --fatal-hints dev/ lib/ test/ tool/ - name: web_engine_integration_test_linux compile_host_script: | From 91ccdbe7f1593f21a224a2859e15e657bac573c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 27 Mar 2020 13:10:39 -0700 Subject: [PATCH 168/386] Add name to the XCTAttachments in the scenario test app (#17355) --- .../ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m index a08519b46b4ec..a3658e337fcca 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/GoldenPlatformViewTests.m @@ -49,11 +49,13 @@ - (void)checkGolden { XCUIScreenshot* screenshot = [[XCUIScreen mainScreen] screenshot]; XCTAttachment* attachment = [XCTAttachment attachmentWithScreenshot:screenshot]; + attachment.name = @"new_golden"; attachment.lifetime = XCTAttachmentLifetimeKeepAlways; [self addAttachment:attachment]; if (golden.image) { XCTAttachment* goldenAttachment = [XCTAttachment attachmentWithImage:golden.image]; + attachment.name = @"current_golden"; goldenAttachment.lifetime = XCTAttachmentLifetimeKeepAlways; [self addAttachment:goldenAttachment]; } else { From 3ee9e3d3785cfd7c0252eff5a84cf98d8a856e10 Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Fri, 27 Mar 2020 14:26:20 -0700 Subject: [PATCH 169/386] [web] changing the integration tests to show errors (#17364) * changing the integration tests to show errors. The drive file is reduced to nothing now * adding more to readme * rebase to master and removing unused import * changing readme --- e2etests/web/regular_integration_tests/README.md | 12 +++++++++--- .../web/regular_integration_tests/pubspec.yaml | 4 ++-- .../test_driver/image_loading_e2e.dart | 1 - .../test_driver/image_loading_e2e_test.dart | 14 ++------------ .../test_driver/text_editing_e2e_test.dart | 16 ++-------------- 5 files changed, 15 insertions(+), 32 deletions(-) diff --git a/e2etests/web/regular_integration_tests/README.md b/e2etests/web/regular_integration_tests/README.md index 7889ef4971482..04f7ca2ed1eaf 100644 --- a/e2etests/web/regular_integration_tests/README.md +++ b/e2etests/web/regular_integration_tests/README.md @@ -5,12 +5,18 @@ configuration (e.g. PWA vs non-PWA packaging), please create another directory under e2etests/web. Otherwise tests such as text_editing, history, scrolling, pointer events... should all go under this package. -# To run the application under test for traouble shooting purposes. +Tests can be run on both 'release' and 'profile' modes. However 'release' mode +will shorten the error. Use 'profile' mode for trouble-shooting purposes where +you can also see the full stack trace. + +# To run the application under test for trouble shooting purposes. flutter run -d web-server lib/text_editing_main.dart --local-engine=host_debug_unopt # To run the Text Editing test and use the developer tools in the browser. -flutter run --target=test_driver/text_editing_e2e.dart -d web-server --web-port=8080 --release --local-engine=host_debug_unopt +flutter run --target=test_driver/text_editing_e2e.dart -d web-server --web-port=8080 --profile --local-engine=host_debug_unopt + +# To test the Text Editing test with driver you either of the following: +flutter drive -v --target=test_driver/text_editing_e2e.dart -d web-server --profile --browser-name=chrome --local-engine=host_debug_unopt -# To test the Text Editing test with driver: flutter drive -v --target=test_driver/text_editing_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt ``` diff --git a/e2etests/web/regular_integration_tests/pubspec.yaml b/e2etests/web/regular_integration_tests/pubspec.yaml index 7f70f838183db..3f0c3280b511b 100644 --- a/e2etests/web/regular_integration_tests/pubspec.yaml +++ b/e2etests/web/regular_integration_tests/pubspec.yaml @@ -13,10 +13,10 @@ dev_dependencies: sdk: flutter flutter_test: sdk: flutter - e2e: 0.2.4+4 + e2e: 0.4.0 http: 0.12.0+2 test: any flutter: assets: - - assets/images/ \ No newline at end of file + - assets/images/ diff --git a/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart index bc6ebb9dba887..3c858da3a9d08 100644 --- a/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart +++ b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:html' as html; -import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:regular_integration_tests/image_loading_main.dart' as app; diff --git a/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart index 02edbe5679179..a29203f7dcdd9 100644 --- a/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart +++ b/e2etests/web/regular_integration_tests/test_driver/image_loading_e2e_test.dart @@ -2,16 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; +import 'package:e2e/e2e_driver.dart' as e2e; -import 'package:flutter_driver/flutter_driver.dart'; - -Future main() async { - final FlutterDriver driver = await FlutterDriver.connect(); - - final String dataRequest = - await driver.requestData(null, timeout: const Duration(seconds: 1)); - await driver.close(); - - exit(dataRequest == 'pass' ? 0 : 1); -} +Future main() async => e2e.main(); diff --git a/e2etests/web/regular_integration_tests/test_driver/text_editing_e2e_test.dart b/e2etests/web/regular_integration_tests/test_driver/text_editing_e2e_test.dart index 26f4278d6505f..a29203f7dcdd9 100644 --- a/e2etests/web/regular_integration_tests/test_driver/text_editing_e2e_test.dart +++ b/e2etests/web/regular_integration_tests/test_driver/text_editing_e2e_test.dart @@ -2,18 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; +import 'package:e2e/e2e_driver.dart' as e2e; -import 'package:flutter_driver/flutter_driver.dart'; - -Future main() async { - final FlutterDriver driver = await FlutterDriver.connect(); - - // TODO(nurhan): https://github.com/flutter/flutter/issues/51940 - final String dataRequest = - await driver.requestData(null, timeout: const Duration(seconds: 1)); - print('result $dataRequest'); - await driver.close(); - - exit(dataRequest == 'pass' ? 0 : 1); -} +Future main() async => e2e.main(); From 4affba4fec83ca1ebd082b8dd5823ffd98b203b9 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 27 Mar 2020 15:24:54 -0700 Subject: [PATCH 170/386] Add keep annotation to Locale.toString (#17376) --- lib/ui/window.dart | 1 + testing/dart/window_hooks_integration_test.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 1c9399b670eaa..d03ece6328364 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -492,6 +492,7 @@ class Locale { /// This identifier happens to be a valid Unicode Locale Identifier using /// underscores as separator, however it is intended to be used for debugging /// purposes only. For parseable results, use [toLanguageTag] instead. + @keepToString @override String toString() { if (!identical(_cachedLocale, this)) { diff --git a/testing/dart/window_hooks_integration_test.dart b/testing/dart/window_hooks_integration_test.dart index 64fd956f19622..17c1bf90890dd 100644 --- a/testing/dart/window_hooks_integration_test.dart +++ b/testing/dart/window_hooks_integration_test.dart @@ -19,6 +19,7 @@ import 'dart:typed_data'; import 'package:test/test.dart'; // HACK: these parts are to get access to private functions tested here. +part '../../lib/ui/annotations.dart'; part '../../lib/ui/channel_buffers.dart'; part '../../lib/ui/compositing.dart'; part '../../lib/ui/geometry.dart'; From 946df02dc6da974c6f46fa46ec554948308081c3 Mon Sep 17 00:00:00 2001 From: Anna Gringauze Date: Fri, 27 Mar 2020 16:09:40 -0700 Subject: [PATCH 171/386] Enable expression evaluation in the debugger for web (#17324) --- flutter_frontend_server/lib/server.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flutter_frontend_server/lib/server.dart b/flutter_frontend_server/lib/server.dart index e0e8bf8e4dff6..85584d20329aa 100644 --- a/flutter_frontend_server/lib/server.dart +++ b/flutter_frontend_server/lib/server.dart @@ -72,7 +72,7 @@ class _FlutterFrontendCompiler implements frontend.CompilerInterface { expression, definitions, typeDefinitions, libraryUri, klass, isStatic); } - // ignore: annotate_overrides + @override Future compileExpressionToJs( String libraryUri, int line, @@ -81,7 +81,8 @@ class _FlutterFrontendCompiler implements frontend.CompilerInterface { Map jsFrameValues, String moduleName, String expression) { - throw UnimplementedError('Compile expression to JS is not supported'); + return _compiler.compileExpressionToJs(libraryUri, line, column, jsModules, + jsFrameValues, moduleName, expression); } @override From b716cbba715a94bd761f0933fce9f116f5c8630b Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 27 Mar 2020 16:39:29 -0700 Subject: [PATCH 172/386] Reland unobstructed platform views (#17336) --- flow/embedded_views.cc | 5 +- flow/embedded_views.h | 5 +- flow/layers/picture_layer.cc | 2 +- flow/layers/picture_layer_unittests.cc | 3 - shell/common/rasterizer.cc | 12 +- .../framework/Source/FlutterOverlayView.mm | 1 + .../framework/Source/FlutterPlatformViews.mm | 355 +++++++++++++----- .../Source/FlutterPlatformViews_Internal.h | 90 ++++- .../Source/FlutterPlatformViews_Internal.mm | 12 +- shell/platform/darwin/ios/ios_surface.h | 5 +- shell/platform/darwin/ios/ios_surface.mm | 12 +- .../embedder_external_view_embedder.cc | 6 +- .../embedder_external_view_embedder.h | 5 +- .../Scenarios.xcodeproj/project.pbxproj | 4 + .../ios/Scenarios/Scenarios/AppDelegate.m | 7 + .../PlatformViewGestureRecognizerTests.m | 6 +- .../UnobstructedPlatformViewTests.m | 254 +++++++++++++ ...tform_view_clippath_iPhone 8_simulator.png | Bin 30022 -> 20295 bytes ...form_view_transform_iPhone 8_simulator.png | Bin 33727 -> 21838 bytes testing/scenario_app/lib/main.dart | 6 + .../scenario_app/lib/src/platform_view.dart | 227 ++++++++++- 21 files changed, 881 insertions(+), 136 deletions(-) create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m diff --git a/flow/embedded_views.cc b/flow/embedded_views.cc index c660f4691318b..5234bf1e50c8c 100644 --- a/flow/embedded_views.cc +++ b/flow/embedded_views.cc @@ -6,10 +6,13 @@ namespace flutter { -bool ExternalViewEmbedder::SubmitFrame(GrContext* context) { +bool ExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { return false; }; +void ExternalViewEmbedder::FinishFrame(){}; + void MutatorsStack::PushClipRect(const SkRect& rect) { std::shared_ptr element = std::make_shared(rect); vector_.push_back(element); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 030eb88c8a06d..7a491a8a152ef 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -248,7 +248,10 @@ class ExternalViewEmbedder { // Must be called on the UI thread. virtual SkCanvas* CompositeEmbeddedView(int view_id) = 0; - virtual bool SubmitFrame(GrContext* context); + virtual bool SubmitFrame(GrContext* context, SkCanvas* background_canvas); + + // This is called after submitting the embedder frame and the surface frame. + virtual void FinishFrame(); FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 3bc7e394c1033..08c09cc9e833b 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -59,7 +59,7 @@ void PictureLayer::Paint(PaintContext& context) const { return; } } - context.leaf_nodes_canvas->drawPicture(picture()); + picture()->playback(context.leaf_nodes_canvas); } } // namespace flutter diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 687c870eeac66..4f565cf500ecc 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -94,9 +94,6 @@ TEST_F(PictureLayerTest, SimplePicture) { 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( layer_offset_matrix)}}, #endif - MockCanvas::DrawCall{ - 1, MockCanvas::DrawPictureData{mock_picture->serialize(), SkPaint(), - SkMatrix()}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 811898b5a42d2..f7e4350fe9c97 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -342,9 +342,17 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { if (raster_status == RasterStatus::kFailed) { return raster_status; } - frame->Submit(); if (external_view_embedder != nullptr) { - external_view_embedder->SubmitFrame(surface_->GetContext()); + external_view_embedder->SubmitFrame(surface_->GetContext(), + root_surface_canvas); + // The external view embedder may mutate the root surface canvas while + // submitting the frame. + // Therefore, submit the final frame after asking the external view + // embedder to submit the frame. + frame->Submit(); + external_view_embedder->FinishFrame(); + } else { + frame->Submit(); } FireNextFrameCallbackIfPresent(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm index 11c0d60618886..4127061f3e7de 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterOverlayView.mm @@ -39,6 +39,7 @@ - (instancetype)init { if (self) { self.layer.opaque = NO; self.userInteractionEnabled = NO; + self.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); } return self; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b86c4623fa7a7..c2cbef18cc167 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -8,16 +8,96 @@ #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/ios_surface_gl.h" +#include #include #include #include #include "FlutterPlatformViews_Internal.h" +#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" namespace flutter { +std::shared_ptr FlutterPlatformViewLayerPool::GetLayer( + GrContext* gr_context, + std::shared_ptr ios_context) { + if (available_layer_index_ >= layers_.size()) { + std::shared_ptr layer; + fml::scoped_nsobject overlay_view; + fml::scoped_nsobject overlay_view_wrapper; + + if (!gr_context) { + overlay_view.reset([[FlutterOverlayView alloc] init]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] init]); + + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(); + + layer = std::make_shared( + std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), + std::move(surface)); + } else { + CGFloat screenScale = [UIScreen mainScreen].scale; + overlay_view.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale]); + overlay_view_wrapper.reset([[FlutterOverlayView alloc] initWithContentsScale:screenScale]); + + std::unique_ptr ios_surface = + [overlay_view.get() createSurface:std::move(ios_context)]; + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + + layer = std::make_shared( + std::move(overlay_view), std::move(overlay_view_wrapper), std::move(ios_surface), + std::move(surface)); + layer->gr_context = gr_context; + } + // The overlay view wrapper masks the overlay view. + // This is required to keep the backing surface size unchanged between frames. + // + // Otherwise, changing the size of the overlay would require a new surface, + // which can be very expensive. + // + // This is the case of an animation in which the overlay size is changing in every frame. + // + // +------------------------+ + // | overlay_view | + // | +--------------+ | +--------------+ + // | | wrapper | | == mask => | overlay_view | + // | +--------------+ | +--------------+ + // +------------------------+ + overlay_view_wrapper.get().clipsToBounds = YES; + [overlay_view_wrapper.get() addSubview:overlay_view]; + layers_.push_back(layer); + } + std::shared_ptr layer = layers_[available_layer_index_]; + if (gr_context != layer->gr_context) { + layer->gr_context = gr_context; + // The overlay already exists, but the GrContext was changed so we need to recreate + // the rendering surface with the new GrContext. + IOSSurface* ios_surface = layer->ios_surface.get(); + std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); + layer->surface = std::move(surface); + } + available_layer_index_++; + return layer; +} + +void FlutterPlatformViewLayerPool::RecycleLayers() { + available_layer_index_ = 0; +} + +std::vector> +FlutterPlatformViewLayerPool::GetUnusedLayers() { + std::vector> results; + for (size_t i = available_layer_index_; i < layers_.size(); i++) { + results.push_back(layers_[i]); + } + return results; +} + void FlutterPlatformViewsController::SetFlutterView(UIView* flutter_view) { flutter_view_.reset([flutter_view retain]); } @@ -83,6 +163,9 @@ NSObject* embedded_view = [factory createWithFrame:CGRectZero viewIdentifier:viewId arguments:params]; + // Set a unique view identifier, so the platform view can be identified in unit tests. + [embedded_view view].accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%ld]", viewId]; views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] @@ -196,8 +279,11 @@ int view_id, std::unique_ptr params) { picture_recorders_[view_id] = std::make_unique(); - picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_)); - picture_recorders_[view_id]->getRecordingCanvas()->clear(SK_ColorTRANSPARENT); + + auto rtree_factory = RTreeFactory(); + platform_view_rtrees_[view_id] = rtree_factory.getInstance(); + picture_recorders_[view_id]->beginRecording(SkRect::Make(frame_size_), &rtree_factory); + composition_order_.push_back(view_id); if (current_composition_params_.count(view_id) == 1 && @@ -357,81 +443,196 @@ [sub_view removeFromSuperview]; } views_.clear(); - overlays_.clear(); composition_order_.clear(); active_composition_order_.clear(); picture_recorders_.clear(); + platform_view_rtrees_.clear(); current_composition_params_.clear(); clip_count_.clear(); views_to_recomposite_.clear(); + layer_pool_->RecycleLayers(); +} + +SkRect FlutterPlatformViewsController::GetPlatformViewRect(int view_id) { + UIView* platform_view = [views_[view_id].get() view]; + UIScreen* screen = [UIScreen mainScreen]; + CGRect platform_view_cgrect = [platform_view convertRect:platform_view.bounds + toView:flutter_view_]; + return SkRect::MakeXYWH(platform_view_cgrect.origin.x * screen.scale, // + platform_view_cgrect.origin.y * screen.scale, // + platform_view_cgrect.size.width * screen.scale, // + platform_view_cgrect.size.height * screen.scale // + ); } bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, - std::shared_ptr ios_context) { + std::shared_ptr ios_context, + SkCanvas* background_canvas) { DisposeViews(); - bool did_submit = true; - for (int64_t view_id : composition_order_) { - EnsureOverlayInitialized(view_id, ios_context, gr_context); - auto frame = overlays_[view_id]->surface->AcquireFrame(frame_size_); - // If frame is null, AcquireFrame already printed out an error message. - if (frame) { - SkCanvas* canvas = frame->SkiaCanvas(); - canvas->drawPicture(picture_recorders_[view_id]->finishRecordingAsPicture()); - canvas->flush(); - did_submit &= frame->Submit(); + // Resolve all pending GPU operations before allocating a new surface. + background_canvas->flush(); + // Clipping the background canvas before drawing the picture recorders requires to + // save and restore the clip context. + SkAutoCanvasRestore save(background_canvas, /*doSave=*/true); + // Maps a platform view id to a vector of `FlutterPlatformViewLayer`. + LayersMap platform_view_layers; + + auto did_submit = true; + auto num_platform_views = composition_order_.size(); + + for (size_t i = 0; i < num_platform_views; i++) { + int64_t platform_view_id = composition_order_[i]; + sk_sp rtree = platform_view_rtrees_[platform_view_id]; + sk_sp picture = picture_recorders_[platform_view_id]->finishRecordingAsPicture(); + + // Check if the current picture contains overlays that intersect with the + // current platform view or any of the previous platform views. + for (size_t j = i + 1; j > 0; j--) { + int64_t current_platform_view_id = composition_order_[j - 1]; + SkRect platform_view_rect = GetPlatformViewRect(current_platform_view_id); + std::list intersection_rects = + rtree->searchNonOverlappingDrawnRects(platform_view_rect); + auto allocation_size = intersection_rects.size(); + + // For testing purposes, the overlay id is used to find the overlay view. + // This is the index of the layer for the current platform view. + auto overlay_id = platform_view_layers[current_platform_view_id].size(); + + // If the max number of allocations per platform view is exceeded, + // then join all the rects into a single one. + // + // TODO(egarciad): Consider making this configurable. + // https://github.com/flutter/flutter/issues/52510 + if (allocation_size > kMaxLayerAllocations) { + SkRect joined_rect; + for (const SkRect& rect : intersection_rects) { + joined_rect.join(rect); + } + // Replace the rects in the intersection rects list for a single rect that is + // the union of all the rects in the list. + intersection_rects.clear(); + intersection_rects.push_back(joined_rect); + } + for (SkRect& joined_rect : intersection_rects) { + // Get the intersection rect between the current rect + // and the platform view rect. + joined_rect.intersect(platform_view_rect); + // Subpixels in the platform may not align with the canvas subpixels. + // To workaround it, round the floating point bounds and make the rect slighly larger. + // For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4, 5}. + joined_rect.setLTRB(std::floor(joined_rect.left()), std::floor(joined_rect.top()), + std::ceil(joined_rect.right()), std::ceil(joined_rect.bottom())); + // Clip the background canvas, so it doesn't contain any of the pixels drawn + // on the overlay layer. + background_canvas->clipRect(joined_rect, SkClipOp::kDifference); + // Get a new host layer. + std::shared_ptr layer = GetLayer(gr_context, // + ios_context, // + picture, // + joined_rect, // + current_platform_view_id, // + overlay_id // + ); + did_submit &= layer->did_submit_last_frame; + platform_view_layers[current_platform_view_id].push_back(layer); + overlay_id++; + } } - } - picture_recorders_.clear(); - if (composition_order_ == active_composition_order_) { - composition_order_.clear(); - return did_submit; - } - DetachUnusedLayers(); - active_composition_order_.clear(); - UIView* flutter_view = flutter_view_.get(); + background_canvas->drawPicture(picture); + } + // If a layer was allocated in the previous frame, but it's not used in the current frame, + // then it can be removed from the scene. + RemoveUnusedLayers(); + // Organize the layers by their z indexes. + BringLayersIntoView(platform_view_layers); + // Mark all layers as available, so they can be used in the next frame. + layer_pool_->RecycleLayers(); + // Reset the composition order, so next frame starts empty. + composition_order_.clear(); + return did_submit; +} + +void FlutterPlatformViewsController::BringLayersIntoView(LayersMap layer_map) { + UIView* flutter_view = flutter_view_.get(); + auto zIndex = 0; for (size_t i = 0; i < composition_order_.size(); i++) { - int view_id = composition_order_[i]; - // We added a chain of super views to the platform view to handle clipping. - // The `platform_view_root` is the view at the top of the chain which is a direct subview of the - // `FlutterView`. - UIView* platform_view_root = root_views_[view_id].get(); - UIView* overlay = overlays_[view_id]->overlay_view; - FML_CHECK(platform_view_root.superview == overlay.superview); - if (platform_view_root.superview == flutter_view) { - [flutter_view bringSubviewToFront:platform_view_root]; - [flutter_view bringSubviewToFront:overlay]; - } else { + int64_t platform_view_id = composition_order_[i]; + std::vector> layers = layer_map[platform_view_id]; + UIView* platform_view_root = root_views_[platform_view_id].get(); + + if (platform_view_root.superview != flutter_view) { [flutter_view addSubview:platform_view_root]; - [flutter_view addSubview:overlay]; - overlay.frame = flutter_view.bounds; + } else { + platform_view_root.layer.zPosition = zIndex++; } - - active_composition_order_.push_back(view_id); + for (const std::shared_ptr& layer : layers) { + if ([layer->overlay_view_wrapper superview] != flutter_view) { + [flutter_view addSubview:layer->overlay_view_wrapper]; + } else { + layer->overlay_view_wrapper.get().layer.zPosition = zIndex++; + } + } + active_composition_order_.push_back(platform_view_id); } - composition_order_.clear(); - return did_submit; } -void FlutterPlatformViewsController::DetachUnusedLayers() { - std::unordered_set composition_order_set; +std::shared_ptr FlutterPlatformViewsController::GetLayer( + GrContext* gr_context, + std::shared_ptr ios_context, + sk_sp picture, + SkRect rect, + int64_t view_id, + int64_t overlay_id) { + std::shared_ptr layer = layer_pool_->GetLayer(gr_context, ios_context); + + UIView* overlay_view_wrapper = layer->overlay_view_wrapper.get(); + auto screenScale = [UIScreen mainScreen].scale; + // Set the size of the overlay view wrapper. + // This wrapper view masks the overlay view. + overlay_view_wrapper.frame = CGRectMake(rect.x() / screenScale, rect.y() / screenScale, + rect.width() / screenScale, rect.height() / screenScale); + // Set a unique view identifier, so the overlay wrapper can be identified in unit tests. + overlay_view_wrapper.accessibilityIdentifier = + [NSString stringWithFormat:@"platform_view[%lld].overlay[%lld]", view_id, overlay_id]; + + UIView* overlay_view = layer->overlay_view.get(); + // Set the size of the overlay view. + // This size is equal to the the device screen size. + overlay_view.frame = flutter_view_.get().bounds; + + std::unique_ptr frame = layer->surface->AcquireFrame(frame_size_); + // If frame is null, AcquireFrame already printed out an error message. + if (!frame) { + return layer; + } + SkCanvas* overlay_canvas = frame->SkiaCanvas(); + overlay_canvas->clear(SK_ColorTRANSPARENT); + // Offset the picture since its absolute position on the scene is determined + // by the position of the overlay view. + overlay_canvas->translate(-rect.x(), -rect.y()); + overlay_canvas->drawPicture(picture); + + layer->did_submit_last_frame = frame->Submit(); + return layer; +} + +void FlutterPlatformViewsController::RemoveUnusedLayers() { + std::vector> layers = layer_pool_->GetUnusedLayers(); + for (const std::shared_ptr& layer : layers) { + [layer->overlay_view_wrapper removeFromSuperview]; + } + std::unordered_set composition_order_set; for (int64_t view_id : composition_order_) { composition_order_set.insert(view_id); } - + // Remove unused platform views. for (int64_t view_id : active_composition_order_) { if (composition_order_set.find(view_id) == composition_order_set.end()) { - if (root_views_.find(view_id) == root_views_.end()) { - continue; - } - // We added a chain of super views to the platform view to handle clipping. - // The `platform_view_root` is the view at the top of the chain which is a direct subview of - // the `FlutterView`. UIView* platform_view_root = root_views_[view_id].get(); [platform_view_root removeFromSuperview]; - [overlays_[view_id]->overlay_view.get() removeFromSuperview]; } } } @@ -447,10 +648,6 @@ views_.erase(viewId); touch_interceptors_.erase(viewId); root_views_.erase(viewId); - if (overlays_.find(viewId) != overlays_.end()) { - [overlays_[viewId]->overlay_view.get() removeFromSuperview]; - } - overlays_.erase(viewId); current_composition_params_.erase(viewId); clip_count_.erase(viewId); views_to_recomposite_.erase(viewId); @@ -458,56 +655,6 @@ views_to_dispose_.clear(); } -void FlutterPlatformViewsController::EnsureOverlayInitialized( - int64_t overlay_id, - std::shared_ptr ios_context, - GrContext* gr_context) { - FML_DCHECK(flutter_view_); - - auto overlay_it = overlays_.find(overlay_id); - - if (!gr_context) { - if (overlays_.count(overlay_id) != 0) { - return; - } - fml::scoped_nsobject overlay_view([[FlutterOverlayView alloc] init]); - overlay_view.get().frame = flutter_view_.get().bounds; - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(); - overlays_[overlay_id] = std::make_unique( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - return; - } - - if (overlay_it != overlays_.end()) { - FlutterPlatformViewLayer* overlay = overlay_it->second.get(); - if (gr_context != overlay->gr_context) { - overlay->gr_context = gr_context; - // The overlay already exists, but the GrContext was changed so we need to recreate - // the rendering surface with the new GrContext. - IOSSurface* ios_surface = overlay_it->second->ios_surface.get(); - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - overlay_it->second->surface = std::move(surface); - } - return; - } - auto contentsScale = flutter_view_.get().layer.contentsScale; - fml::scoped_nsobject overlay_view( - [[FlutterOverlayView alloc] initWithContentsScale:contentsScale]); - overlay_view.get().frame = flutter_view_.get().bounds; - overlay_view.get().autoresizingMask = - (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); - std::unique_ptr ios_surface = - [overlay_view.get() createSurface:std::move(ios_context)]; - std::unique_ptr surface = ios_surface->CreateGPUSurface(gr_context); - overlays_[overlay_id] = std::make_unique( - std::move(overlay_view), std::move(ios_surface), std::move(surface)); - overlays_[overlay_id]->gr_context = gr_context; -} - } // namespace flutter // This recognizers delays touch events from being dispatched to the responder chain until it failed diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index d135d7d2ac290..c5ebd9a479b1f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_ #include "flutter/flow/embedded_views.h" +#include "flutter/flow/rtree.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" @@ -59,21 +60,63 @@ class IOSSurface; struct FlutterPlatformViewLayer { FlutterPlatformViewLayer(fml::scoped_nsobject overlay_view, + fml::scoped_nsobject overlay_view_wrapper, std::unique_ptr ios_surface, std::unique_ptr surface); ~FlutterPlatformViewLayer(); fml::scoped_nsobject overlay_view; + fml::scoped_nsobject overlay_view_wrapper; std::unique_ptr ios_surface; std::unique_ptr surface; + // Whether a frame for this layer was submitted. + bool did_submit_last_frame; + // The GrContext that is currently used by the overlay surfaces. // We track this to know when the GrContext for the Flutter app has changed // so we can update the overlay with the new context. GrContext* gr_context; }; +// This class isn't thread safe. +class FlutterPlatformViewLayerPool { + public: + FlutterPlatformViewLayerPool() = default; + ~FlutterPlatformViewLayerPool() = default; + + // Gets a layer from the pool if available, or allocates a new one. + // Finally, it marks the layer as used. That is, it increments `available_layer_index_`. + std::shared_ptr GetLayer(GrContext* gr_context, + std::shared_ptr ios_context); + + // Gets the layers in the pool that aren't currently used. + // This method doesn't mark the layers as unused. + std::vector> GetUnusedLayers(); + + // Marks the layers in the pool as available for reuse. + void RecycleLayers(); + + private: + // The index of the entry in the layers_ vector that determines the beginning of the unused + // layers. For example, consider the following vector: + // _____ + // | 0 | + /// |---| + /// | 1 | <-- available_layer_index_ + /// |---| + /// | 2 | + /// |---| + /// + /// This indicates that entries starting from 1 can be reused meanwhile the entry at position 0 + /// cannot be reused. + size_t available_layer_index_ = 0; + std::vector> layers_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewLayerPool); +}; + class FlutterPlatformViewsController { public: FlutterPlatformViewsController(); @@ -109,14 +152,37 @@ class FlutterPlatformViewsController { SkCanvas* CompositeEmbeddedView(int view_id); + // The rect of the platform view at index view_id. This rect has been translated into the + // host view coordinate system. Units are device screen pixels. + SkRect GetPlatformViewRect(int view_id); + // Discards all platform views instances and auxiliary resources. void Reset(); - bool SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context); + bool SubmitFrame(GrContext* gr_context, + std::shared_ptr ios_context, + SkCanvas* background_canvas); void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: + static const size_t kMaxLayerAllocations = 2; + + using LayersMap = std::map>>; + + // The pool of reusable view layers. The pool allows to recycle layer in each frame. + std::unique_ptr layer_pool_; + + // The platform view's R-tree keyed off the view id, which contains any subsequent + // draw operation until the next platform view or the last leaf node in the layer tree. + // + // The R-trees are deleted by the FlutterPlatformViewsController.reset(). + std::map> platform_view_rtrees_; + + // The platform view's picture recorder keyed off the view id, which contains any subsequent + // operation until the next platform view or the end of the last leaf node in the layer tree. + std::map> picture_recorders_; + fml::scoped_nsobject channel_; fml::scoped_nsobject flutter_view_; fml::scoped_nsobject flutter_view_controller_; @@ -134,7 +200,6 @@ class FlutterPlatformViewsController { // Mapping a platform view ID to the count of the clipping operations that were applied to the // platform view last time it was composited. std::map clip_count_; - std::map> overlays_; SkISize frame_size_; // This is the number of frames the task runners will stay @@ -163,19 +228,12 @@ class FlutterPlatformViewsController { std::map gesture_recognizers_blocking_policies; - std::map> picture_recorders_; - void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); - - void DetachUnusedLayers(); // Dispose the views in `views_to_dispose_`. void DisposeViews(); - void EnsureOverlayInitialized(int64_t overlay_id, - std::shared_ptr ios_context, - GrContext* gr_context); // This will return true after pre-roll if any of the embedded views // have mutated for last layer tree. @@ -215,6 +273,20 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from + // the picture on the layer's canvas. + std::shared_ptr GetLayer(GrContext* gr_context, + std::shared_ptr ios_context, + sk_sp picture, + SkRect rect, + int64_t view_id, + int64_t overlay_id); + // Removes overlay views and platform views that aren't needed in the current frame. + void RemoveUnusedLayers(); + // Appends the overlay views and platform view and sets their z index based on the composition + // order. + void BringLayersIntoView(LayersMap layer_map); + FML_DISALLOW_COPY_AND_ASSIGN(FlutterPlatformViewsController); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm index 9310fa1803f11..551535a2c7faf 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.mm @@ -11,16 +11,20 @@ namespace flutter { -FlutterPlatformViewLayer::FlutterPlatformViewLayer(fml::scoped_nsobject overlay_view, - std::unique_ptr ios_surface, - std::unique_ptr surface) +FlutterPlatformViewLayer::FlutterPlatformViewLayer( + fml::scoped_nsobject overlay_view, + fml::scoped_nsobject overlay_view_wrapper, + std::unique_ptr ios_surface, + std::unique_ptr surface) : overlay_view(std::move(overlay_view)), + overlay_view_wrapper(std::move(overlay_view_wrapper)), ios_surface(std::move(ios_surface)), surface(std::move(surface)){}; FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default; -FlutterPlatformViewsController::FlutterPlatformViewsController() = default; +FlutterPlatformViewsController::FlutterPlatformViewsController() + : layer_pool_(std::make_unique()){}; FlutterPlatformViewsController::~FlutterPlatformViewsController() = default; diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 58233d8a2f656..8cba9285cfb02 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -77,7 +77,10 @@ class IOSSurface : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context) override; + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index fe85c77f12c2d..f51160e94e471 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -132,12 +132,18 @@ bool IsIosEmbeddedViewsPreviewEnabled() { } // |ExternalViewEmbedder| -bool IOSSurface::SubmitFrame(GrContext* context) { +bool IOSSurface::SubmitFrame(GrContext* context, SkCanvas* background_canvas) { TRACE_EVENT0("flutter", "IOSSurface::SubmitFrame"); FML_CHECK(platform_views_controller_ != nullptr); - bool submitted = platform_views_controller_->SubmitFrame(std::move(context), ios_context_); - [CATransaction commit]; + bool submitted = + platform_views_controller_->SubmitFrame(std::move(context), ios_context_, background_canvas); return submitted; } +// |ExternalViewEmbedder| +void IOSSurface::FinishFrame() { + TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + [CATransaction commit]; +} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.cc b/shell/platform/embedder/embedder_external_view_embedder.cc index 5e77073e7ff47..23658888f7caa 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.cc +++ b/shell/platform/embedder/embedder_external_view_embedder.cc @@ -129,7 +129,8 @@ static FlutterBackingStoreConfig MakeBackingStoreConfig( } // |ExternalViewEmbedder| -bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { +bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { auto [matched_render_targets, pending_keys] = render_target_cache_.GetExistingTargetsInCache(pending_views_); @@ -265,4 +266,7 @@ bool EmbedderExternalViewEmbedder::SubmitFrame(GrContext* context) { return true; } +// |ExternalViewEmbedder| +void EmbedderExternalViewEmbedder::FinishFrame() {} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_external_view_embedder.h b/shell/platform/embedder/embedder_external_view_embedder.h index 7000d2cde04cd..63c944a88d7ef 100644 --- a/shell/platform/embedder/embedder_external_view_embedder.h +++ b/shell/platform/embedder/embedder_external_view_embedder.h @@ -89,7 +89,10 @@ class EmbedderExternalViewEmbedder final : public ExternalViewEmbedder { SkCanvas* CompositeEmbeddedView(int view_id) override; // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context) override; + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; // |ExternalViewEmbedder| SkCanvas* GetRootCanvas() override; diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index c24333a3a8a7f..818d902b3e2e9 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 3DEF491A23C3BE6500184216 /* golden_platform_view_transform_iPhone 8_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE09E9123C010BD006C9851 /* golden_platform_view_transform_iPhone 8_simulator.png */; }; 59A97FD8236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */; }; 59A97FDA236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */; }; + 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */; }; 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */; }; 6816DBA12317573300A51400 /* GoldenImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA02317573300A51400 /* GoldenImage.m */; }; 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */; }; @@ -149,6 +150,7 @@ 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone 8_simulator.png"; sourceTree = ""; }; 59A97FD7236A49D300B4C066 /* golden_platform_view_multiple_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_iPhone SE_simulator.png"; sourceTree = ""; }; 59A97FD9236B984300B4C066 /* golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_multiple_background_foreground_iPhone SE_simulator.png"; sourceTree = ""; }; + 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnobstructedPlatformViewTests.m; sourceTree = ""; }; 6816DB9C231750ED00A51400 /* GoldenPlatformViewTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenPlatformViewTests.h; sourceTree = ""; }; 6816DB9D231750ED00A51400 /* GoldenPlatformViewTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GoldenPlatformViewTests.m; sourceTree = ""; }; 6816DB9F2317573300A51400 /* GoldenImage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GoldenImage.h; sourceTree = ""; }; @@ -245,6 +247,7 @@ 248D76ED22E388380012F0C1 /* ScenariosUITests */ = { isa = PBXGroup; children = ( + 6402EBD024147BDA00987DCB /* UnobstructedPlatformViewTests.m */, 0D14A3FD239743190013D873 /* golden_platform_view_rotate_iPhone SE_simulator.png */, 3DE09E8B23C010BC006C9851 /* golden_platform_view_clippath_iPhone 8_simulator.png */, 3DE09E9223C010BD006C9851 /* golden_platform_view_cliprect_iPhone 8_simulator.png */, @@ -488,6 +491,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6402EBD124147BDA00987DCB /* UnobstructedPlatformViewTests.m in Sources */, 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 348889b19b856..9bd732f647039 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -29,6 +29,13 @@ - (BOOL)application:(UIApplication*)application // the launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. NSDictionary* launchArgsMap = @{ @"--platform-view" : @"platform_view", + @"--platform-view-no-overlay-intersection" : @"platform_view_no_overlay_intersection", + @"--platform-view-two-intersecting-overlays" : @"platform_view_two_intersecting_overlays", + @"--platform-view-partial-intersection" : @"platform_view_partial_intersection", + @"--platform-view-one-overlay-two-intersecting-overlays" : + @"platform_view_one_overlay_two_intersecting_overlays", + @"--platform-view-multiple-without-overlays" : @"platform_view_multiple_without_overlays", + @"--platform-view-max-overlays" : @"platform_view_max_overlays", @"--platform-view-multiple" : @"platform_view_multiple", @"--platform-view-multiple-background-foreground" : @"platform_view_multiple_background_foreground", diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m index d791210f22707..3d583e1d5e824 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -25,7 +25,7 @@ - (void)testRejectPolicyUtilTouchesEnded { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"platform_view"]; + return [element.identifier hasPrefix:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { @@ -56,7 +56,7 @@ - (void)testRejectPolicyEager { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"platform_view"]; + return [element.identifier hasPrefix:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { @@ -91,7 +91,7 @@ - (void)testAccept { [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary* _Nullable bindings) { XCUIElement* element = evaluatedObject; - return [element.identifier isEqualToString:@"platform_view"]; + return [element.identifier hasPrefix:@"platform_view"]; }]; XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m new file mode 100644 index 0000000000000..02e7eee35f098 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/UnobstructedPlatformViewTests.m @@ -0,0 +1,254 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +@interface UnobstructedPlatformViewTests : XCTestCase + +@end + +@implementation UnobstructedPlatformViewTests + +- (void)setUp { + self.continueAfterFailure = NO; +} + +// A is the layer, which z index is higher than the platform view. +// +--------+ +// | PV | +---+ +// +--------+ | A | +// +---+ +- (void)testNoOverlay { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-no-overlay-intersection" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertFalse(overlay.exists); +} + +// A is the layer above the platform view. +// +-----------------+ +// | PV +---+ | +// | | A | | +// | +---+ | +// +-----------------+ +- (void)testOneOverlay { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 150); + XCTAssertEqual(overlay.frame.origin.y, 150); + XCTAssertEqual(overlay.frame.size.width, 50); + XCTAssertEqual(overlay.frame.size.height, 50); +} + +// A is the layer above the platform view. +// +-----------------+ +// | PV +---+ | +// +-----------| A |-+ +// +---+ +- (void)testOneOverlayPartialIntersection { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-partial-intersection" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 200); + XCTAssertEqual(overlay.frame.origin.y, 250); + XCTAssertEqual(overlay.frame.size.width, 50); + // Half the height of the overlay. + XCTAssertEqual(overlay.frame.size.height, 25); +} + +// A and B are the layers above the platform view. +// +--------------------+ +// | PV +------------+ | +// | | B +-----+ | | +// | +---| A |-+ | +// +----------| |---+ +// +-----+ +- (void)testTwoIntersectingOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-two-intersecting-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 150); + XCTAssertEqual(overlay.frame.origin.y, 150); + XCTAssertEqual(overlay.frame.size.width, 75); + XCTAssertEqual(overlay.frame.size.height, 75); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); +} + +// A, B, and C are the layers above the platform view. +// +-------------------------+ +// | PV +-----------+ | +// | +---+ | B +-----+ | | +// | | C | +---| A |-+ | +// | +---+ +-----+ | +// +-------------------------+ +- (void)testOneOverlayAndTwoIntersectingOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-one-overlay-two-intersecting-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay1 = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay1.exists); + XCTAssertEqual(overlay1.frame.origin.x, 150); + XCTAssertEqual(overlay1.frame.origin.y, 150); + XCTAssertEqual(overlay1.frame.size.width, 75); + XCTAssertEqual(overlay1.frame.size.height, 75); + + XCUIElement* overlay2 = app.otherElements[@"platform_view[0].overlay[1]"]; + XCTAssertTrue(overlay2.exists); + XCTAssertEqual(overlay2.frame.origin.x, 75); + XCTAssertEqual(overlay2.frame.origin.y, 225); + XCTAssertEqual(overlay2.frame.size.width, 50); + XCTAssertEqual(overlay2.frame.size.height, 50); +} + +// A is the layer, which z index is higher than the platform view. +// +--------+ +// | PV | +---+ +// +--------+ | A | +// +--------+ +---+ +// | PV | +// +--------+ +- (void)testMultiplePlatformViewsWithoutOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-multiple-without-overlays" ]; + [app launch]; + + XCUIElement* platform_view1 = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view1.exists); + XCTAssertEqual(platform_view1.frame.origin.x, 25); + XCTAssertEqual(platform_view1.frame.origin.y, 325); + XCTAssertEqual(platform_view1.frame.size.width, 250); + XCTAssertEqual(platform_view1.frame.size.height, 250); + + XCUIElement* platform_view2 = app.textViews[@"platform_view[1]"]; + XCTAssertTrue(platform_view2.exists); + XCTAssertEqual(platform_view2.frame.origin.x, 25); + XCTAssertEqual(platform_view2.frame.origin.y, 25); + XCTAssertEqual(platform_view2.frame.size.width, 250); + XCTAssertEqual(platform_view2.frame.size.height, 250); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[0]"].exists); + XCTAssertFalse(app.otherElements[@"platform_view[1].overlay[0]"].exists); +} + +// A is the layer above both platform view. +// +------------+ +// | PV +----+ | +// +-----| A |-+ +// +-----| |-+ +// | PV +----+ | +// +------------+ +- (void)testMultiplePlatformViewsWithOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-multiple-background-foreground" ]; + [app launch]; + + XCUIElement* platform_view1 = app.textViews[@"platform_view[8]"]; + XCTAssertTrue(platform_view1.exists); + XCTAssertEqual(platform_view1.frame.origin.x, 25); + XCTAssertEqual(platform_view1.frame.origin.y, 325); + XCTAssertEqual(platform_view1.frame.size.width, 250); + XCTAssertEqual(platform_view1.frame.size.height, 250); + + XCUIElement* platform_view2 = app.textViews[@"platform_view[9]"]; + XCTAssertTrue(platform_view2.exists); + XCTAssertEqual(platform_view2.frame.origin.x, 25); + XCTAssertEqual(platform_view2.frame.origin.y, 25); + XCTAssertEqual(platform_view2.frame.size.width, 250); + XCTAssertEqual(platform_view2.frame.size.height, 250); + + XCUIElement* overlay1 = app.otherElements[@"platform_view[8].overlay[0]"]; + XCTAssertTrue(overlay1.exists); + XCTAssertEqual(overlay1.frame.origin.x, 25); + XCTAssertEqual(overlay1.frame.origin.y, 325); + XCTAssertEqual(overlay1.frame.size.width, 225); + XCTAssertEqual(overlay1.frame.size.height, 175); + + XCUIElement* overlay2 = app.otherElements[@"platform_view[9].overlay[0]"]; + XCTAssertTrue(overlay2.exists); + XCTAssertEqual(overlay2.frame.origin.x, 25); + XCTAssertEqual(overlay2.frame.origin.y, 25); + XCTAssertEqual(overlay2.frame.size.width, 225); + XCTAssertEqual(overlay2.frame.size.height, 250); +} + +// More then two overlays are merged into a single layer. +// +---------------------+ +// | +---+ +---+ +---+ | +// | | A | | B | | C | | +// | +---+ +---+ +---+ | +// | +-------+ | +// +-| D |-----------+ +// +-------+ +- (void)testPlatformViewsMaxOverlays { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--platform-view-max-overlays" ]; + [app launch]; + + XCUIElement* platform_view = app.textViews[@"platform_view[0]"]; + XCTAssertTrue(platform_view.exists); + XCTAssertEqual(platform_view.frame.origin.x, 25); + XCTAssertEqual(platform_view.frame.origin.y, 25); + XCTAssertEqual(platform_view.frame.size.width, 250); + XCTAssertEqual(platform_view.frame.size.height, 250); + + XCUIElement* overlay = app.otherElements[@"platform_view[0].overlay[0]"]; + XCTAssertTrue(overlay.exists); + XCTAssertEqual(overlay.frame.origin.x, 75); + XCTAssertEqual(overlay.frame.origin.y, 85); + XCTAssertEqual(overlay.frame.size.width, 150); + XCTAssertEqual(overlay.frame.size.height, 190); + + XCTAssertFalse(app.otherElements[@"platform_view[0].overlay[1]"].exists); +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_clippath_iPhone 8_simulator.png index a193faeb040223e56343903c777f9181edb5bcc0..9ec19ab474f03b6cc7786cb038ee3b3e6d0c51a4 100644 GIT binary patch literal 20295 zcmeHuX*iVa8@HyJnz3Yzr3E$ieMzzvCQH_kA+oDbk8B}3O<5*oDSL~pkUjg(q>u%s=030UI)CT+JD2M|+}G1lqoY1XO+`gTr=hN* zPelcrrJ_PeAz|PX)smMN!DXMTzM2wMUK`sa_>ZTJv4-u13si#O9!Z7RhoRaJJp%qx z?K?(=pxjeYY3^hF_g;S=|DR{Tnjtr+;D4Sm0axfxB=`Z+f3C1(*xx5$v&sAa-Xo-- zN15f^xWEPFtbW;*ii(8?`q`(U&pi)5WV@lFYXGj$#-Yno@DKm)6}p!srTj#Pg6l&K zm2(E3`=@BVmW3Qoo zEAE=V()MiS$ENStBiA{d&TI4$Uu9`u;g8Qjx^L@!$Qxhsl75%>84gx2Zkt9p zRMxIl)orxYi*C=;2XrpKI37oy58lCFSM-=&@xF=oTnrW3#;5$UiQAH?R`gyXGY?cu zrq>A6T!x{2ge|-jD(&p3OzZK`!Cx68%Q=PKgb{}sqw2|M$ zql9LA7VneSbIERwo4HO~Pv=4E+IARkA+J5RTrU5ixIMDd-Gj|3_n70e{kVRPqm%YbIrEf=)%WS0xw@4K>=zfOA>tKgx*wpiLK}#s8`F)Z8tF7a!t7z7<4>CBITKz z!{_m5FWzUiD$~o$dd}A|mHK|YyyH4I(Bw_OI$M<_R7_9vIen|xbfYUnVx`aZZ%(5}4Y;&(4*>&L_jytIj{Ezty%+*$Wvj~__PCB!-R41mQZ@4VB z(*JF1Rc{KP5FV~<_snHf?8gPCb<3b_3%5`07E%0D?E@JS){IU*z3tSj^fc0a9lab$ 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 literal 30022 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ΠAr29j`n?8;-QZE z4jSFDu$|OBK+1@&kY%gDyh4f+et*UW!xwP*gQNm<3K_Yi(Xv2`G0u{G@CDL_`Za|R z0&ba{A^qz!h73(?gBQ zaOaF%@BB*^QHtUx^a&4#swelbeQ@;feaTS45;*zevVw|@B_*Du_fg$vTTApLhT z;(~N8NauodE=cEg)wx}DZdaZ22H-9H%#@2te_y_G)xmx1@#nP+_vs7wCD1Qx;69A{ z-@X&;4$L>JfxjakBUrV-?pPC7bYAj`F8b4z{e%L^L5)Ap&Ej}pv9OANp0FJk6lCu2 z`qwMCQ-p#C$T2zwwupZl2?j`8#a1jqb& z0=E@=#V?uu=M`PWHYtj}0{Y_#&Wfu+&iGHHKM&yc@?V(~vi|c9FG*CCezoXdPx+n% zIcrwd|B-1q@2k?|%s;R2x|fU6rSJXgF(6k7@Me>%1g;WLALUvCG}*aI;3|QWvbdJO zwFIsuaDJV@wFIsua4msbJ8%mq_&GOM30x(hZ4Is^K;6hy0#^x~-^Fq*;eTWaOX`;t XeZBM9{0{PeD?M`X*ny1wR>A)dG2_6W diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png b/testing/scenario_app/ios/Scenarios/ScenariosUITests/golden_platform_view_transform_iPhone 8_simulator.png index 793082f8f0f7c4edaa29fd14c5ee5a782d1e69b1..0db030ed20983c5e65e59f07d139ba527cefb8ef 100644 GIT binary patch literal 21838 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 literal 33727 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%0D$f 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 HeDr?+Nh376 diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 494d6585aa7fe..0ab3f7c5352f1 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -19,6 +19,12 @@ import 'src/touches_scenario.dart'; Map _scenarios = { 'animated_color_square': AnimatedColorSquareScenario(window), 'platform_view': PlatformViewScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_no_overlay_intersection': PlatformViewNoOverlayIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_partial_intersection': PlatformViewPartialIntersectionScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_two_intersecting_overlays': PlatformViewTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_one_overlay_two_intersecting_overlays': PlatformViewOneOverlayTwoIntersectingOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_multiple_without_overlays': MultiPlatformViewWithoutOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), + 'platform_view_max_overlays': PlatformViewMaxOverlaysScenario(window, 'Hello from Scenarios (Platform View)', id: 0), 'platform_view_cliprect': PlatformViewClipRectScenario(window, 'PlatformViewClipRect', id: 1), 'platform_view_cliprrect': PlatformViewClipRRectScenario(window, 'PlatformViewClipRRect', id: 2), 'platform_view_clippath': PlatformViewClipPathScenario(window, 'PlatformViewClipPath', id: 3), diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 0dde98fd9eebb..fcea41a65f7d5 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -48,6 +48,224 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin } } +/// A simple platform view with overlay that doesn't intersect with the platform view. +class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewNoOverlayIntersectionScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + finishBuilderByAddingPlatformViewAndPicture( + builder, + 0, + overlayOffset: const Offset(150, 350), + ); + } +} + +/// A simple platform view with an overlay that partially intersects with the platform view. +class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewPartialIntersectionScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + finishBuilderByAddingPlatformViewAndPicture( + builder, + 0, + overlayOffset: const Offset(150, 250), + ); + } +} + +/// A simple platform view with two overlays that intersect with each other and the platform view. +class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// A simple platform view with one overlay and two overlays that intersect with each other and the platform view. +class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewOneOverlayTwoIntersectingOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, 200), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// Two platform views without an overlay intersecting either platform view. +class MultiPlatformViewWithoutOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + MultiPlatformViewWithoutOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + builder.pushOffset(0, 600); + _addPlatformViewtoScene(builder, 0, 500, 500); + builder.pop(); + + _addPlatformViewtoScene(builder, 1, 500, 500); + + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawRect( + const Rect.fromLTRB(0, 0, 100, 1000), + Paint()..color = const Color(0xFFFF0000), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(580, 0), picture); + + builder.pop(); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + +/// A simple platform view with too many overlays result in a single native view. +class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewMaxOverlaysScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + + _addPlatformViewtoScene(builder, 0, 500, 500); + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.drawCircle( + const Offset(50, 50), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(100, 100), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, 200), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + canvas.drawCircle( + const Offset(-100, -80), + 50, + Paint()..color = const Color(0xFFABCDEF), + ); + final Picture picture = recorder.endRecording(); + builder.addPicture(const Offset(300, 300), picture); + final Scene scene = builder.build(); + window.render(scene); + scene.dispose(); + } +} + /// Builds a scene with 2 platform views. class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. @@ -424,12 +642,17 @@ mixin _BasePlatformViewScenarioMixin on Scenario { } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { + void finishBuilderByAddingPlatformViewAndPicture( + SceneBuilder sceneBuilder, + int viewId, { + Offset overlayOffset, + }) { + overlayOffset ??= const Offset(50, 50); _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawCircle( - const Offset(50, 50), + overlayOffset, 50, Paint()..color = const Color(0xFFABCDEF), ); From a973ade3313de7f196d54a9d803315e250ddc96b Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Fri, 27 Mar 2020 16:52:02 -0700 Subject: [PATCH 173/386] change chrome version (#17382) * change chrome version * change chrome version --- lib/web_ui/dev/browser_lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/web_ui/dev/browser_lock.yaml b/lib/web_ui/dev/browser_lock.yaml index 970cf1feb5596..2bede5bcd221a 100644 --- a/lib/web_ui/dev/browser_lock.yaml +++ b/lib/web_ui/dev/browser_lock.yaml @@ -1,7 +1,7 @@ chrome: # It seems Chrome can't always release from the same build for all operating # systems, so we specify per-OS build number. - Linux: 735129 + Linux: 753189 Mac: 735116 Win: 735105 firefox: From f3d9f9a950eb5b115d33705922bc2ec47a2f7eb5 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 28 Mar 2020 12:08:05 -0400 Subject: [PATCH 174/386] Roll src/third_party/skia 385de38b272f..7345d88277c4 (8 commits) (#17373) https://skia.googlesource.com/skia.git/+log/385de38b272f..7345d88277c4 git log 385de38b272f..7345d88277c4 --date=short --first-parent --format='%ad %ae %s' 2020-03-27 bungeman@google.com gn format gn/BUILD.gn 2020-03-27 brianosman@google.com Re-generate source from FP files after clang-format change 2020-03-27 brianosman@google.com Remove arbitrary limit on number of composed color filters 2020-03-27 brianosman@google.com Remove GrSRGBEffect 2020-03-27 brianosman@google.com Disable BinPackParameters in .clang-format 2020-03-27 bungeman@google.com Add back deprecated warnings. 2020-03-27 phanm@google.com Add experimental support for svg text tag. 2020-03-27 robertphillips@google.com Have GrFillRRectOp make use of the GrSimpleMeshDrawOpHelper Created with: gclient setdep -r src/third_party/skia@7345d88277c4 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 03098f531e4c4..5161795463030 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '385de38b272f6355fb0911ece994ffa71e7a35c2', + 'skia_revision': '7345d88277c4796ff9875aa7f1be4810b8461c2b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 015bfe293d6c8..f509945478a7b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: eea4d9938ea13509a3f4156d3aac0a12 +Signature: b0ea5feef6396c23f492abb5fab4cf58 UNUSED LICENSES: @@ -641,8 +641,6 @@ FILE: ../../../third_party/skia/src/gpu/GrTextureRenderTargetProxy.h FILE: ../../../third_party/skia/src/gpu/GrUserStencilSettings.h FILE: ../../../third_party/skia/src/gpu/GrWindowRectangles.h FILE: ../../../third_party/skia/src/gpu/GrWindowRectsState.h -FILE: ../../../third_party/skia/src/gpu/effects/GrSRGBEffect.cpp -FILE: ../../../third_party/skia/src/gpu/effects/GrSRGBEffect.h FILE: ../../../third_party/skia/src/gpu/effects/GrShadowGeoProc.cpp FILE: ../../../third_party/skia/src/gpu/effects/GrShadowGeoProc.h FILE: ../../../third_party/skia/src/gpu/geometry/GrShape.cpp @@ -2997,6 +2995,8 @@ FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoDecoder.cpp FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoDecoder.h FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoEncoder.cpp FILE: ../../../third_party/skia/experimental/ffmpeg/SkVideoEncoder.h +FILE: ../../../third_party/skia/experimental/svg/model/SkSVGText.cpp +FILE: ../../../third_party/skia/experimental/svg/model/SkSVGText.h FILE: ../../../third_party/skia/gm/backdrop.cpp FILE: ../../../third_party/skia/gm/backdrop_imagefilter_croprect.cpp FILE: ../../../third_party/skia/gm/bug9331.cpp From 361d5d53206e78572aa545155ddd074bfd55ea77 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Sun, 29 Mar 2020 15:46:39 -0700 Subject: [PATCH 175/386] Partially revert Samsung keyboard force-update workaround (#17391) --- .../editing/InputConnectionAdaptor.java | 19 -------- .../plugin/editing/TextInputPluginTest.java | 44 ------------------- 2 files changed, 63 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index 0fda81fd8930c..7513b2ffa4d57 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -155,25 +155,6 @@ public boolean finishComposingText() { CursorAnchorInfo anchorInfo = builder.build(); mImm.updateCursorAnchorInfo(mFlutterView, anchorInfo); } - // TODO(garyq): There is still a duplication case that comes from hiding+showing the keyboard. - // The exact behavior to cause it has so far been hard to pinpoint and it happens far more - // rarely than the original bug. - - // Temporarily indicate to the IME that the composing region selection should be reset. - // The correct selection is then immediately set properly in the updateEditingState() call - // in this method. This is a hack to trigger Samsung keyboard's internal cache to clear. - // This prevents duplication on keyboard hide+show. See - // https://github.com/flutter/flutter/issues/31512 - // - // We only do this if the proper selection will be restored later, eg, when mBatchCount is 0. - if (mBatchCount == 0) { - mImm.updateSelection( - mFlutterView, - -1, /*selStart*/ - -1, /*selEnd*/ - -1, /*candidatesStart*/ - -1 /*candidatesEnd*/); - } } updateEditingState(); diff --git a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java index 51dbc603b9793..b2ecfd7b13fd6 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -29,8 +29,6 @@ import io.flutter.plugin.platform.PlatformViewsController; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.junit.Test; @@ -349,48 +347,6 @@ public void inputConnection_finishComposingTextUpdatesIMM() throws JSONException } } - @Test - public void inputConnection_samsungFinishComposingTextSetsSelection() throws JSONException { - ShadowBuild.setManufacturer("samsung"); - InputMethodSubtype inputMethodSubtype = - new InputMethodSubtype(0, 0, /*locale=*/ "en", "", "", false, false); - Settings.Secure.putString( - RuntimeEnvironment.application.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, - "com.sec.android.inputmethod/.SamsungKeypad"); - TestImm testImm = - Shadow.extract( - RuntimeEnvironment.application.getSystemService(Context.INPUT_METHOD_SERVICE)); - testImm.setCurrentInputMethodSubtype(inputMethodSubtype); - FlutterJNI mockFlutterJni = mock(FlutterJNI.class); - View testView = new View(RuntimeEnvironment.application); - DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class))); - TextInputPlugin textInputPlugin = - new TextInputPlugin(testView, dartExecutor, mock(PlatformViewsController.class)); - textInputPlugin.setTextInputClient( - 0, - new TextInputChannel.Configuration( - false, - false, - true, - TextInputChannel.TextCapitalization.NONE, - new TextInputChannel.InputType(TextInputChannel.TextInputType.TEXT, false, false), - null, - null)); - // There's a pending restart since we initialized the text input client. Flush that now. - textInputPlugin.setTextInputEditingState( - testView, new TextInputChannel.TextEditState("", 0, 0)); - InputConnection connection = textInputPlugin.createInputConnection(testView, new EditorInfo()); - - testImm.setTrackSelection(true); - connection.finishComposingText(); - testImm.setTrackSelection(false); - - List expectedSelectionValues = - Arrays.asList(0, 0, -1, -1, -1, -1, -1, -1, 0, 0, -1, -1); - assertEquals(testImm.getSelectionUpdateValues(), expectedSelectionValues); - } - @Implements(InputMethodManager.class) public static class TestImm extends ShadowInputMethodManager { private InputMethodSubtype currentInputMethodSubtype; From 7bd8dee5beb123e8c2aa916bd58532cb1953674b Mon Sep 17 00:00:00 2001 From: Yegor Date: Sun, 29 Mar 2020 19:23:37 -0700 Subject: [PATCH 176/386] fix implicit dynamic and two lints everywhere; implicit cast in dev/ (#17372) --- lib/web_ui/analysis_options.yaml | 6 +- lib/web_ui/dev/analysis_options.yaml | 7 + lib/web_ui/dev/browser.dart | 22 ++- lib/web_ui/dev/build.dart | 12 +- lib/web_ui/dev/chrome_installer.dart | 4 +- lib/web_ui/dev/clean.dart | 5 +- lib/web_ui/dev/common.dart | 22 +-- lib/web_ui/dev/edge_installation.dart | 4 +- lib/web_ui/dev/felt.dart | 2 +- lib/web_ui/dev/firefox_installer.dart | 8 +- lib/web_ui/dev/goldens.dart | 12 +- lib/web_ui/dev/integration_tests_manager.dart | 2 +- lib/web_ui/dev/safari_installation.dart | 2 +- lib/web_ui/dev/test_platform.dart | 139 ++++++++++++------ lib/web_ui/dev/test_runner.dart | 18 ++- lib/web_ui/dev/utils.dart | 31 +++- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 4 +- lib/web_ui/lib/src/engine/clipboard.dart | 6 +- .../engine/compositor/canvas_kit_canvas.dart | 2 +- .../src/engine/compositor/color_filter.dart | 2 +- .../src/engine/compositor/embedded_views.dart | 28 +++- .../lib/src/engine/compositor/fonts.dart | 7 +- .../lib/src/engine/compositor/surface.dart | 8 +- .../lib/src/engine/compositor/vertices.dart | 8 +- lib/web_ui/lib/src/engine/dom_renderer.dart | 2 +- .../lib/src/engine/html_image_codec.dart | 2 +- .../lib/src/engine/render_vertices.dart | 70 ++++----- lib/web_ui/lib/src/engine/shadow.dart | 2 +- lib/web_ui/lib/src/engine/text/ruler.dart | 4 +- lib/web_ui/lib/src/ui/canvas.dart | 4 +- lib/web_ui/lib/src/ui/text.dart | 8 +- lib/web_ui/lib/src/ui/window.dart | 8 +- lib/web_ui/test/engine/history_test.dart | 4 +- .../test/engine/pointer_binding_test.dart | 12 +- .../test/engine/recording_canvas_test.dart | 6 +- .../engine/canvas_blend_golden_test.dart | 2 +- .../engine/canvas_clip_path_test.dart | 2 +- .../engine/canvas_draw_image_golden_test.dart | 2 +- 38 files changed, 303 insertions(+), 186 deletions(-) create mode 100644 lib/web_ui/dev/analysis_options.yaml diff --git a/lib/web_ui/analysis_options.yaml b/lib/web_ui/analysis_options.yaml index 7c46e201fff84..5b40ea7c85c1e 100644 --- a/lib/web_ui/analysis_options.yaml +++ b/lib/web_ui/analysis_options.yaml @@ -3,9 +3,9 @@ # uncommented, we'll delete this file and simply inherit the root options. analyzer: - # TODO(uncomment) strong-mode: + strong-mode: # TODO(uncomment) implicit-casts: false - # TODO(uncomment) implicit-dynamic: false + implicit-dynamic: false errors: missing_required_param: warning missing_return: warning @@ -15,7 +15,7 @@ analyzer: linter: rules: - always_declare_return_types - # TODO(uncomment) - always_put_control_body_on_new_line + - always_put_control_body_on_new_line # TODO(uncomment) - always_specify_types # TODO(uncomment) - annotate_overrides # TODO(uncomment) - avoid_classes_with_only_static_members diff --git a/lib/web_ui/dev/analysis_options.yaml b/lib/web_ui/dev/analysis_options.yaml new file mode 100644 index 0000000000000..ac1dc7ce40a6a --- /dev/null +++ b/lib/web_ui/dev/analysis_options.yaml @@ -0,0 +1,7 @@ +# This is a temporary file used to clean up dev/ before other directories +include: ../analysis_options.yaml + +analyzer: + strong-mode: + implicit-casts: false + implicit-dynamic: false diff --git a/lib/web_ui/dev/browser.dart b/lib/web_ui/dev/browser.dart index 989609cd8a5bf..71d7de547a603 100644 --- a/lib/web_ui/dev/browser.dart +++ b/lib/web_ui/dev/browser.dart @@ -51,8 +51,8 @@ abstract class Browser { /// /// If there's a problem starting or running the browser, this will complete /// with an error. - Future get onExit => _onExitCompleter.future; - final _onExitCompleter = Completer(); + Future get onExit => _onExitCompleter.future; + final _onExitCompleter = Completer(); /// Standard IO streams for the underlying browser process. final _ioSubscriptions = []; @@ -96,7 +96,7 @@ abstract class Browser { // resolve the ambiguity is to wait a brief amount of time and see if this // browser is actually closed. if (!_closed && exitCode < 0) { - await Future.delayed(Duration(milliseconds: 200)); + await Future.delayed(Duration(milliseconds: 200)); } if (!_closed && exitCode != 0) { @@ -110,15 +110,21 @@ abstract class Browser { } _onExitCompleter.complete(); - }, onError: (error, StackTrace stackTrace) { + }, onError: (dynamic error, StackTrace stackTrace) { // Ignore any errors after the browser has been closed. - if (_closed) return; + if (_closed) { + return; + } // Make sure the process dies even if the error wasn't fatal. _process.then((process) => process.kill()); - if (stackTrace == null) stackTrace = Trace.current(); - if (_onExitCompleter.isCompleted) return; + if (stackTrace == null) { + stackTrace = Trace.current(); + } + if (_onExitCompleter.isCompleted) { + return; + } _onExitCompleter.completeError( Exception('Failed to run $name: ${getErrorMessage(error)}.'), stackTrace); @@ -142,6 +148,6 @@ abstract class Browser { (await _process).kill(); // Swallow exceptions. The user should explicitly use [onExit] for these. - return onExit.catchError((_) {}); + return onExit.catchError((dynamic _) {}); } } diff --git a/lib/web_ui/dev/build.dart b/lib/web_ui/dev/build.dart index a6c70405964bb..7e31a3fdb2399 100644 --- a/lib/web_ui/dev/build.dart +++ b/lib/web_ui/dev/build.dart @@ -13,7 +13,7 @@ import 'package:watcher/watcher.dart'; import 'environment.dart'; import 'utils.dart'; -class BuildCommand extends Command { +class BuildCommand extends Command with ArgUtils { BuildCommand() { argParser ..addFlag( @@ -35,15 +35,9 @@ class BuildCommand extends Command { @override String get description => 'Build the Flutter web engine.'; - bool get isWatchMode => argResults['watch']; + bool get isWatchMode => boolArg('watch'); - int getNinjaJobCount() { - final String ninjaJobsArg = argResults['ninja-jobs']; - if (ninjaJobsArg != null) { - return int.tryParse(ninjaJobsArg); - } - return null; - } + int getNinjaJobCount() => intArg('ninja-jobs'); @override FutureOr run() async { diff --git a/lib/web_ui/dev/chrome_installer.dart b/lib/web_ui/dev/chrome_installer.dart index 95a3892581b38..b6345ea379858 100644 --- a/lib/web_ui/dev/chrome_installer.dart +++ b/lib/web_ui/dev/chrome_installer.dart @@ -45,7 +45,7 @@ class ChromeArgParser extends BrowserArgParser { @override void parseOptions(ArgResults argResults) { - _version = argResults['chrome-version']; + _version = argResults['chrome-version'] as String; } @override @@ -108,7 +108,7 @@ Future _findSystemChromeExecutable() async { 'Failed to locate system Chrome installation.'); } - return which.stdout; + return which.stdout as String; } /// Manages the installation of a particular [version] of Chrome. diff --git a/lib/web_ui/dev/clean.dart b/lib/web_ui/dev/clean.dart index 712e4c28e2a3e..ba2da00c857f6 100644 --- a/lib/web_ui/dev/clean.dart +++ b/lib/web_ui/dev/clean.dart @@ -10,8 +10,9 @@ import 'package:args/command_runner.dart'; import 'package:path/path.dart' as path; import 'environment.dart'; +import 'utils.dart'; -class CleanCommand extends Command { +class CleanCommand extends Command with ArgUtils { CleanCommand() { argParser ..addFlag( @@ -24,7 +25,7 @@ class CleanCommand extends Command { @override String get name => 'clean'; - bool get _alsoCleanNinja => argResults['ninja']; + bool get _alsoCleanNinja => boolArg('ninja'); @override String get description => 'Deletes build caches and artifacts.'; diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 37b8e2bf93473..3939bf2e9b6cd 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -71,8 +71,8 @@ const String _kBaseDownloadUrl = class _WindowsBinding implements PlatformBinding { @override int getChromeBuild(YamlMap browserLock) { - final YamlMap chromeMap = browserLock['chrome']; - return chromeMap['Win']; + final YamlMap chromeMap = browserLock['chrome'] as YamlMap; + return chromeMap['Win'] as int; } @override @@ -110,8 +110,8 @@ class _WindowsBinding implements PlatformBinding { class _LinuxBinding implements PlatformBinding { @override int getChromeBuild(YamlMap browserLock) { - final YamlMap chromeMap = browserLock['chrome']; - return chromeMap['Linux']; + final YamlMap chromeMap = browserLock['chrome'] as YamlMap; + return chromeMap['Linux'] as int; } @override @@ -151,8 +151,8 @@ class _LinuxBinding implements PlatformBinding { class _MacBinding implements PlatformBinding { @override int getChromeBuild(YamlMap browserLock) { - final YamlMap chromeMap = browserLock['chrome']; - return chromeMap['Mac']; + final YamlMap chromeMap = browserLock['chrome'] as YamlMap; + return chromeMap['Mac'] as int; } @override @@ -192,10 +192,10 @@ class _MacBinding implements PlatformBinding { } class BrowserInstallation { - const BrowserInstallation( - {@required this.version, - @required this.executable, - fetchLatestChromeVersion}); + const BrowserInstallation({ + @required this.version, + @required this.executable, + }); /// Browser version. final String version; @@ -230,7 +230,7 @@ class BrowserLock { BrowserLock._() { final io.File lockFile = io.File( path.join(environment.webUiRootDir.path, 'dev', 'browser_lock.yaml')); - this._configuration = loadYaml(lockFile.readAsStringSync()); + this._configuration = loadYaml(lockFile.readAsStringSync()) as YamlMap; } } diff --git a/lib/web_ui/dev/edge_installation.dart b/lib/web_ui/dev/edge_installation.dart index 3190474164d00..efdf03f30b1b1 100644 --- a/lib/web_ui/dev/edge_installation.dart +++ b/lib/web_ui/dev/edge_installation.dart @@ -36,7 +36,7 @@ class EdgeArgParser extends BrowserArgParser { @override void parseOptions(ArgResults argResults) { - _version = argResults['edge-version']; + _version = argResults['edge-version'] as String; assert(_version == 'system'); } @@ -124,7 +124,7 @@ class EdgeLauncher { EdgeLauncher() : version = - BrowserLock.instance.configuration['edge']['launcher_version']; + BrowserLock.instance.configuration['edge']['launcher_version'] as String; /// Install the launcher if it does not exist in this system. void install() async { diff --git a/lib/web_ui/dev/felt.dart b/lib/web_ui/dev/felt.dart index 9b860872f3387..3ba2984777d65 100644 --- a/lib/web_ui/dev/felt.dart +++ b/lib/web_ui/dev/felt.dart @@ -32,7 +32,7 @@ void main(List args) async { _listenToShutdownSignals(); try { - final bool result = await runner.run(args); + final bool result = (await runner.run(args)) as bool; if (result == false) { print('Sub-command returned false: `${args.join(' ')}`'); _cleanup(); diff --git a/lib/web_ui/dev/firefox_installer.dart b/lib/web_ui/dev/firefox_installer.dart index d3043e9f68303..5a5c951c719dd 100644 --- a/lib/web_ui/dev/firefox_installer.dart +++ b/lib/web_ui/dev/firefox_installer.dart @@ -26,7 +26,7 @@ class FirefoxArgParser extends BrowserArgParser { @override void populateOptions(ArgParser argParser) { final YamlMap browserLock = BrowserLock.instance.configuration; - String firefoxVersion = browserLock['firefox']['version']; + String firefoxVersion = browserLock['firefox']['version'] as String; argParser ..addOption( @@ -42,7 +42,7 @@ class FirefoxArgParser extends BrowserArgParser { @override void parseOptions(ArgResults argResults) { - _version = argResults['firefox-version']; + _version = argResults['firefox-version'] as String; } @override @@ -260,7 +260,7 @@ class FirefoxInstaller { 'Exit code ${mountResult.exitCode}.\n${mountResult.stderr}'); } - List processOutput = mountResult.stdout.split('\n'); + List processOutput = (mountResult.stdout as String).split('\n'); String volumePath = _volumeFromMountResult(processOutput); if (volumePath == null) { throw BrowserInstallerException( @@ -313,7 +313,7 @@ Future _findSystemFirefoxExecutable() async { throw BrowserInstallerException( 'Failed to locate system Firefox installation.'); } - return which.stdout; + return which.stdout as String; } /// Fetches the latest available Firefox build version on Linux. diff --git a/lib/web_ui/dev/goldens.dart b/lib/web_ui/dev/goldens.dart index 4e61ecb805799..fef621c724385 100644 --- a/lib/web_ui/dev/goldens.dart +++ b/lib/web_ui/dev/goldens.dart @@ -72,10 +72,10 @@ class ImageDiff { /// That would be the distance between black and white. static final double _maxTheoreticalColorDistance = Color.distance( - [255, 255, 255], // white - [0, 0, 0], // black + [255, 255, 255], // white + [0, 0, 0], // black false, - ); + ).toDouble(); // If the normalized color difference of a pixel is greater than this number, // we consider it a wrong pixel. @@ -203,9 +203,9 @@ class _GoldensRepoFetcher { final io.File lockFile = io.File( path.join(environment.webUiDevDir.path, 'goldens_lock.yaml') ); - final YamlMap lock = loadYaml(lockFile.readAsStringSync()); - _repository = lock['repository']; - _revision = lock['revision']; + final YamlMap lock = loadYaml(lockFile.readAsStringSync()) as YamlMap; + _repository = lock['repository'] as String; + _revision = lock['revision'] as String; final String localRevision = await _getLocalRevision(); if (localRevision == _revision) { diff --git a/lib/web_ui/dev/integration_tests_manager.dart b/lib/web_ui/dev/integration_tests_manager.dart index 8a7967784c966..811e876664541 100644 --- a/lib/web_ui/dev/integration_tests_manager.dart +++ b/lib/web_ui/dev/integration_tests_manager.dart @@ -133,7 +133,7 @@ class IntegrationTestsManager { Future _runTestsInDirectory(io.Directory directory) async { final io.Directory testDirectory = io.Directory(pathlib.join(directory.path, 'test_driver')); - final List entities = testDirectory + final List entities = testDirectory .listSync(followLinks: false) .whereType() .toList(); diff --git a/lib/web_ui/dev/safari_installation.dart b/lib/web_ui/dev/safari_installation.dart index 05b0065d05fed..95c7dc2332edb 100644 --- a/lib/web_ui/dev/safari_installation.dart +++ b/lib/web_ui/dev/safari_installation.dart @@ -35,7 +35,7 @@ class SafariArgParser extends BrowserArgParser { @override void parseOptions(ArgResults argResults) { - _version = argResults['safari-version']; + _version = argResults['safari-version'] as String; assert(_version == 'system'); } diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index 1e5aa3c79b7b8..922b432cabe04 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -152,21 +152,29 @@ class BrowserPlatform extends PlatformPlugin { } final String payload = await request.readAsString(); - final Map requestData = json.decode(payload); - final String filename = requestData['filename']; - final bool write = requestData['write']; + final Map requestData = + json.decode(payload) as Map; + final String filename = requestData['filename'] as String; + final bool write = requestData['write'] as bool; final double maxDiffRate = requestData.containsKey('maxdiffrate') - ? requestData['maxdiffrate'].toDouble() // can be parsed as either int or double - : kMaxDiffRateFailure; - final Map region = requestData['region']; - final PixelComparison pixelComparison = PixelComparison.values.firstWhere((value) => value.toString() == requestData['pixelComparison']); - final String result = await _diffScreenshot(filename, write, maxDiffRate, region, pixelComparison); + ? (requestData['maxdiffrate'] as num) + .toDouble() // can be parsed as either int or double + : kMaxDiffRateFailure; + final Map region = + requestData['region'] as Map; + final PixelComparison pixelComparison = PixelComparison.values.firstWhere( + (value) => value.toString() == requestData['pixelComparison']); + final String result = await _diffScreenshot( + filename, write, maxDiffRate, region, pixelComparison); return shelf.Response.ok(json.encode(result)); } Future _diffScreenshot( - String filename, bool write, double maxDiffRateFailure, - Map region, PixelComparison pixelComparison) async { + String filename, + bool write, + double maxDiffRateFailure, + Map region, + PixelComparison pixelComparison) async { if (doUpdateScreenshotGoldens) { write = true; } @@ -209,9 +217,9 @@ To automatically create this file call matchGoldenFile('$filename', write: true) Map captureScreenshotParameters = null; if (region != null) { - captureScreenshotParameters = { + captureScreenshotParameters = { 'format': 'png', - 'clip': { + 'clip': { 'x': region['x'], 'y': region['y'], 'width': region['width'], @@ -224,7 +232,8 @@ To automatically create this file call matchGoldenFile('$filename', write: true) // Setting hardware-independent screen parameters: // https://chromedevtools.github.io/devtools-protocol/tot/Emulation - await wipConnection.sendCommand('Emulation.setDeviceMetricsOverride', { + await wipConnection + .sendCommand('Emulation.setDeviceMetricsOverride', { 'width': kMaxScreenshotWidth, 'height': kMaxScreenshotHeight, 'deviceScaleFactor': 1, @@ -234,7 +243,8 @@ To automatically create this file call matchGoldenFile('$filename', write: true) 'Page.captureScreenshot', captureScreenshotParameters); // Compare screenshots - final Image screenshot = decodePng(base64.decode(response.result['data'])); + final Image screenshot = + decodePng(base64.decode(response.result['data'] as String)); if (write) { // Don't even bother with the comparison, just write and return @@ -406,17 +416,25 @@ Golden file $filename did not match the image generated by the test. '.'); } - if (_closed) return null; + if (_closed) { + return null; + } Uri suiteUrl = url.resolveUri( p.toUri(p.withoutExtension(p.relative(path, from: _root)) + '.html')); - if (_closed) return null; + if (_closed) { + return null; + } var browserManager = await _browserManagerFor(browser); - if (_closed || browserManager == null) return null; + if (_closed || browserManager == null) { + return null; + } var suite = await browserManager.load(path, suiteUrl, suiteConfig, message); - if (_closed) return null; + if (_closed) { + return null; + } return suite; } @@ -429,14 +447,16 @@ Golden file $filename did not match the image generated by the test. /// /// If no browser manager is running yet, starts one. Future _browserManagerFor(Runtime browser) { - if (_browserManager != null) return _browserManager; + if (_browserManager != null) { + return _browserManager; + } var completer = Completer.sync(); var path = _webSocketHandler.create(webSocketHandler(completer.complete)); var webSocketUrl = url.replace(scheme: 'ws').resolve(path); var hostUrl = (_config.pubServeUrl == null ? url : _config.pubServeUrl) .resolve('packages/web_engine_tester/static/index.html') - .replace(queryParameters: { + .replace(queryParameters: { 'managerUrl': webSocketUrl.toString(), 'debug': _config.pauseAfterLoad.toString() }); @@ -446,7 +466,7 @@ Golden file $filename did not match the image generated by the test. // Store null values for browsers that error out so we know not to load them // again. - _browserManager = future.catchError((_) => null); + _browserManager = future.catchError((dynamic _) => null); return future; } @@ -485,7 +505,7 @@ Golden file $filename did not match the image generated by the test. }); } - final _closeMemo = AsyncMemoizer(); + final AsyncMemoizer _closeMemo = AsyncMemoizer(); } /// A Shelf handler that provides support for one-time handlers. @@ -518,11 +538,15 @@ class OneOffHandler { /// Dispatches [request] to the appropriate handler. FutureOr _onRequest(shelf.Request request) { var components = p.url.split(request.url.path); - if (components.isEmpty) return shelf.Response.notFound(null); + if (components.isEmpty) { + return shelf.Response.notFound(null); + } var path = components.removeAt(0); var handler = _handlers.remove(path); - if (handler == null) return shelf.Response.notFound(null); + if (handler == null) { + return shelf.Response.notFound(null); + } return handler(request.change(path: path)); } } @@ -563,13 +587,19 @@ class PathHandler { var components = p.url.split(request.url.path); for (var i = 0; i < components.length; i++) { node = node.children[components[i]]; - if (node == null) break; - if (node.handler == null) continue; + if (node == null) { + break; + } + if (node.handler == null) { + continue; + } handler = node.handler; handlerIndex = i; } - if (handler == null) return shelf.Response.notFound('Not found.'); + if (handler == null) { + return shelf.Response.notFound('Not found.'); + } return handler( request.change(path: p.url.joinAll(components.take(handlerIndex + 1)))); @@ -623,7 +653,7 @@ class BrowserManager { CancelableCompleter _pauseCompleter; /// The controller for [_BrowserEnvironment.onRestart]. - final _onRestartController = StreamController.broadcast(); + final _onRestartController = StreamController.broadcast(); /// The environment to attach to each suite. Future<_BrowserEnvironment> _environment; @@ -660,17 +690,23 @@ class BrowserManager { browser.onExit.then((_) { throw Exception('${runtime.name} exited before connecting.'); - }).catchError((error, StackTrace stackTrace) { - if (completer.isCompleted) return; + }).catchError((dynamic error, StackTrace stackTrace) { + if (completer.isCompleted) { + return; + } completer.completeError(error, stackTrace); }); future.then((webSocket) { - if (completer.isCompleted) return; + if (completer.isCompleted) { + return; + } completer.complete(BrowserManager._(browser, runtime, webSocket)); - }).catchError((error, StackTrace stackTrace) { + }).catchError((dynamic error, StackTrace stackTrace) { browser.close(); - if (completer.isCompleted) return; + if (completer.isCompleted) { + return; + } completer.completeError(error, stackTrace); }); @@ -683,8 +719,7 @@ class BrowserManager { /// Starts the browser identified by [browser] using [settings] and has it load [url]. /// /// If [debug] is true, starts the browser in debug mode. - static Browser _newBrowser(Uri url, Runtime browser, - {bool debug = false}) { + static Browser _newBrowser(Uri url, Runtime browser, {bool debug = false}) { return SupportedBrowsers.instance.getBrowser(browser, url, debug: debug); } @@ -706,10 +741,12 @@ class BrowserManager { // Whenever we get a message, no matter which child channel it's for, we the // know browser is still running code which means the user isn't debugging. - _channel = MultiChannel( + _channel = MultiChannel( webSocket.cast().transform(jsonDocument).changeStream((stream) { return stream.map((message) { - if (!_closed) _timer.reset(); + if (!_closed) { + _timer.reset(); + } for (var controller in _controllers) { controller.setDebugging(false); } @@ -720,7 +757,7 @@ class BrowserManager { _environment = _loadBrowserEnvironment(); _channel.stream - .listen((message) => _onMessage(message as Map), onDone: close); + .listen((dynamic message) => _onMessage(message as Map), onDone: close); } /// Loads [_BrowserEnvironment]. @@ -737,7 +774,7 @@ class BrowserManager { Future load(String path, Uri url, SuiteConfiguration suiteConfig, Object message) async { url = url.replace( - fragment: Uri.encodeFull(jsonEncode({ + fragment: Uri.encodeFull(jsonEncode({ 'metadata': suiteConfig.metadata.serialize(), 'browser': _runtime.identifier }))); @@ -745,7 +782,9 @@ class BrowserManager { var suiteID = _suiteID++; RunnerSuiteController controller; void closeIframe() { - if (_closed) return; + if (_closed) { + return; + } _controllers.remove(controller); _channel.sink.add({'command': 'closeSuite', 'id': suiteID}); } @@ -754,8 +793,8 @@ class BrowserManager { // case we should unload the iframe. var virtualChannel = _channel.virtualChannel(); var suiteChannelID = virtualChannel.id; - var suiteChannel = virtualChannel - .transformStream(StreamTransformer.fromHandlers(handleDone: (sink) { + var suiteChannel = virtualChannel.transformStream( + StreamTransformer.fromHandlers(handleDone: (sink) { closeIframe(); sink.close(); })); @@ -797,9 +836,11 @@ class BrowserManager { /// An implementation of [Environment.displayPause]. CancelableOperation _displayPause() { - if (_pauseCompleter != null) return _pauseCompleter.operation; + if (_pauseCompleter != null) { + return _pauseCompleter.operation; + } - _pauseCompleter = CancelableCompleter(onCancel: () { + _pauseCompleter = CancelableCompleter(onCancel: () { _channel.sink.add({'command': 'resume'}); _pauseCompleter = null; }); @@ -824,7 +865,9 @@ class BrowserManager { break; case 'resume': - if (_pauseCompleter != null) _pauseCompleter.complete(); + if (_pauseCompleter != null) { + _pauseCompleter.complete(); + } break; default: @@ -839,12 +882,14 @@ class BrowserManager { Future close() => _closeMemoizer.runOnce(() { _closed = true; _timer.cancel(); - if (_pauseCompleter != null) _pauseCompleter.complete(); + if (_pauseCompleter != null) { + _pauseCompleter.complete(); + } _pauseCompleter = null; _controllers.clear(); return _browser.close(); }); - final _closeMemoizer = AsyncMemoizer(); + final AsyncMemoizer _closeMemoizer = AsyncMemoizer(); } /// An implementation of [Environment] for the browser. diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 8ca3fa13e3530..5f5995c3d6465 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -33,7 +33,7 @@ enum TestTypesRequested { all, } -class TestCommand extends Command { +class TestCommand extends Command with ArgUtils { TestCommand() { argParser ..addFlag( @@ -84,13 +84,13 @@ class TestCommand extends Command { /// Check the flags to see what type of tests are requested. TestTypesRequested findTestType() { - if (argResults['unit-tests-only'] && argResults['integration-tests-only']) { + if (boolArg('unit-tests-only') && boolArg('integration-tests-only')) { throw ArgumentError('Conflicting arguments: unit-tests-only and ' 'integration-tests-only are both set'); - } else if (argResults['unit-tests-only']) { + } else if (boolArg('unit-tests-only')) { print('Running the unit tests only'); return TestTypesRequested.unit; - } else if (argResults['integration-tests-only']) { + } else if (boolArg('integration-tests-only')) { if (!isChrome) { throw UnimplementedError( 'Integration tests are only available on Chrome Desktop for now'); @@ -163,7 +163,7 @@ class TestCommand extends Command { /// /// In this mode the browser pauses before running the test to allow /// you set breakpoints or inspect the code. - bool get isDebug => argResults['debug']; + bool get isDebug => boolArg('debug'); /// Paths to targets to run, e.g. a single test. List get targets => argResults.rest; @@ -178,13 +178,15 @@ class TestCommand extends Command { /// Whether all tests should run. bool get runAllTests => targets.isEmpty; - String get browser => argResults['browser']; + /// The name of the browser to run tests in. + String get browser => stringArg('browser'); - bool get isChrome => argResults['browser'] == 'chrome'; + /// Whether [browser] is set to "chrome". + bool get isChrome => browser == 'chrome'; /// When running screenshot tests writes them to the file system into /// ".dart_tool/goldens". - bool get doUpdateScreenshotGoldens => argResults['update-screenshot-goldens']; + bool get doUpdateScreenshotGoldens => boolArg('update-screenshot-goldens'); Future _runTargetTests(List targets) async { await _runTestBatch(targets, concurrency: 1, expectFailure: false); diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index 5b7e6f306d9a5..4cf53b6651a62 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:io' as io; +import 'package:args/command_runner.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; @@ -97,14 +98,14 @@ Future evalProcess( ); if (result.exitCode != 0) { throw ProcessException( - description: result.stderr, + description: result.stderr as String, executable: executable, arguments: arguments, workingDirectory: workingDirectory, exitCode: result.exitCode, ); } - return result.stdout; + return result.stdout as String; } @immutable @@ -134,3 +135,29 @@ class ProcessException implements Exception { return '$message'; } } + +/// Adds utility methods +mixin ArgUtils on Command { + /// Extracts a boolean argument from [argResults]. + bool boolArg(String name) => argResults[name] as bool; + + /// Extracts a string argument from [argResults]. + String stringArg(String name) => argResults[name] as String; + + /// Extracts a integer argument from [argResults]. + /// + /// If the argument value cannot be parsed as [int] throws an [ArgumentError]. + int intArg(String name) { + final String rawValue = stringArg(name); + if (rawValue == null) { + return null; + } + final int value = int.tryParse(rawValue); + if (value == null) { + throw ArgumentError( + 'Argument $name should be an integer value but was "$rawValue"', + ); + } + return value; + } +} diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index c9fdb211b9677..f14fedb652a6a 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -768,6 +768,8 @@ List _clipContent(List<_SaveClipEntry> clipStack, } String _maskFilterToCss(ui.MaskFilter maskFilter) { - if (maskFilter == null) return 'none'; + if (maskFilter == null) { + return 'none'; + } return 'blur(${maskFilter.webOnlySigma}px)'; } diff --git a/lib/web_ui/lib/src/engine/clipboard.dart b/lib/web_ui/lib/src/engine/clipboard.dart index fb82e4a566aeb..de119a2842e73 100644 --- a/lib/web_ui/lib/src/engine/clipboard.dart +++ b/lib/web_ui/lib/src/engine/clipboard.dart @@ -27,7 +27,7 @@ class ClipboardMessageHandler { callback(codec.encodeErrorEnvelope( code: 'copy_fail', message: 'Clipboard.setData failed')); } - }).catchError((_) { + }).catchError((dynamic _) { callback(codec.encodeErrorEnvelope( code: 'copy_fail', message: 'Clipboard.setData failed')); }); @@ -37,9 +37,9 @@ class ClipboardMessageHandler { void getDataMethodCall(ui.PlatformMessageResponseCallback callback) { const MethodCodec codec = JSONMethodCodec(); _pasteFromClipboardStrategy.getData().then((String data) { - final Map map = {'text': data}; + final Map map = {'text': data}; callback(codec.encodeSuccessEnvelope(map)); - }).catchError((error) { + }).catchError((dynamic error) { print('Could not get text from clipboard: $error'); callback(codec.encodeErrorEnvelope( code: 'paste_fail', message: 'Clipboard.getData failed')); diff --git a/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart b/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart index f58ed7e76dfb4..0aca26c381f11 100644 --- a/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart +++ b/lib/web_ui/lib/src/engine/compositor/canvas_kit_canvas.dart @@ -145,7 +145,7 @@ class CanvasKitCanvas implements ui.Canvas { _drawLine(p1, p2, paint); } - void _drawLine(ui.Offset p1, ui.Offset p2, paint) { + void _drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) { _canvas.drawLine(p1, p2, paint); } diff --git a/lib/web_ui/lib/src/engine/compositor/color_filter.dart b/lib/web_ui/lib/src/engine/compositor/color_filter.dart index 8a5857066942d..38aecfee0cadc 100644 --- a/lib/web_ui/lib/src/engine/compositor/color_filter.dart +++ b/lib/web_ui/lib/src/engine/compositor/color_filter.dart @@ -19,7 +19,7 @@ class SkColorFilter { SkColorFilter.matrix(EngineColorFilter filter) { // TODO(het): Find a way to remove these array conversions. - final js.JsArray colorMatrix = js.JsArray(); + final js.JsArray colorMatrix = js.JsArray(); colorMatrix.length = 20; for (int i = 0; i < 20; i++) { colorMatrix[i] = filter._matrix[i]; diff --git a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart index 9deee4f7b60f5..4cb6e64b7a74a 100644 --- a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart @@ -316,7 +316,9 @@ class HtmlViewEmbedder { /// Ensures we add a container of SVG path defs to the DOM so they can /// be referred to in clip-path: url(#blah). void _ensureSvgPathDefs() { - if (_svgPathDefs != null) return; + if (_svgPathDefs != null) { + return; + } _svgPathDefs = html.Element.html( '', treeSanitizer: _NullTreeSanitizer(), @@ -399,8 +401,12 @@ class EmbeddedViewParams { final MutatorsStack mutators; bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (other is! EmbeddedViewParams) return false; + if (identical(this, other)) { + return true; + } + if (other is! EmbeddedViewParams) { + return false; + } EmbeddedViewParams typedOther = other; return offset == typedOther.offset && @@ -456,8 +462,12 @@ class Mutator { double get alphaFloat => alpha / 255.0; bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (other is! Mutator) return false; + if (identical(this, other)) { + return true; + } + if (other is! Mutator) { + return false; + } final Mutator typedOther = other; if (type != typedOther.type) { @@ -517,8 +527,12 @@ class MutatorsStack extends Iterable { } bool operator ==(dynamic other) { - if (identical(other, this)) return true; - if (other is! MutatorsStack) return false; + if (identical(other, this)) { + return true; + } + if (other is! MutatorsStack) { + return false; + } final MutatorsStack typedOther = other; if (_mutators.length != typedOther._mutators.length) { diff --git a/lib/web_ui/lib/src/engine/compositor/fonts.dart b/lib/web_ui/lib/src/engine/compositor/fonts.dart index f6d23be754c96..99c62fa9e91e1 100644 --- a/lib/web_ui/lib/src/engine/compositor/fonts.dart +++ b/lib/web_ui/lib/src/engine/compositor/fonts.dart @@ -47,7 +47,9 @@ class SkiaFontCollection { /// Loads all of the unloaded fonts in [_unloadedFonts] and adds them /// to [_registeredFonts]. Future _loadFonts() async { - if (_unloadedFonts.isEmpty) return; + if (_unloadedFonts.isEmpty) { + return; + } final List<_RegisteredFont> loadedFonts = await Future.wait(_unloadedFonts); _registeredFonts.addAll(loadedFonts.where((x) => x != null)); @@ -174,7 +176,8 @@ class SkiaFontCollection { } Future _getArrayBuffer(dynamic fetchResult) { - return fetchResult.arrayBuffer().then((x) => x as ByteBuffer); + // TODO(yjbanov): fetchResult.arrayBuffer is a dynamic invocation. Clean it up. + return fetchResult.arrayBuffer().then((dynamic x) => x as ByteBuffer); } js.JsObject skFontMgr; diff --git a/lib/web_ui/lib/src/engine/compositor/surface.dart b/lib/web_ui/lib/src/engine/compositor/surface.dart index 3e5afed3fe64e..20f6bfe5532a8 100644 --- a/lib/web_ui/lib/src/engine/compositor/surface.dart +++ b/lib/web_ui/lib/src/engine/compositor/surface.dart @@ -50,7 +50,9 @@ class Surface { final SkSurface surface = acquireRenderSurface(size); canvasKit.callMethod('setCurrentContext', [surface.context]); - if (surface == null) return null; + if (surface == null) { + return null; + } SubmitCallback submitCallback = (SurfaceFrame surfaceFrame, SkCanvas canvas) { @@ -115,7 +117,7 @@ class Surface { final int glContext = canvasKit .callMethod('GetWebGLContext', [htmlCanvas]); final js.JsObject grContext = - canvasKit.callMethod('MakeGrContext', [glContext]); + canvasKit.callMethod('MakeGrContext', [glContext]); final js.JsObject skSurface = canvasKit.callMethod('MakeOnScreenGLSurface', [ grContext, @@ -137,7 +139,7 @@ class Surface { return false; } - canvasKit.callMethod('setCurrentContext', [_surface.context]); + canvasKit.callMethod('setCurrentContext', [_surface.context]); _surface.getCanvas().flush(); return true; } diff --git a/lib/web_ui/lib/src/engine/compositor/vertices.dart b/lib/web_ui/lib/src/engine/compositor/vertices.dart index b9d43ba6d262b..6cde7a8ec8374 100644 --- a/lib/web_ui/lib/src/engine/compositor/vertices.dart +++ b/lib/web_ui/lib/src/engine/compositor/vertices.dart @@ -8,7 +8,9 @@ part of engine; Int32List _encodeColorList(List colors) { final int colorCount = colors.length; final Int32List result = Int32List(colorCount); - for (int i = 0; i < colorCount; ++i) result[i] = colors[i].value; + for (int i = 0; i < colorCount; ++i) { + result[i] = colors[i].value; + } return result; } @@ -117,7 +119,9 @@ class SkVertices implements ui.Vertices { } static js.JsArray> _encodePoints(List points) { - if (points == null) return null; + if (points == null) { + return null; + } js.JsArray> encodedPoints = js.JsArray>(); diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index 5990ad2f49d7a..b6f1cd06c4366 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -71,7 +71,7 @@ class DomRenderer { /// This getter calls the `hasFocus` method of the `Document` interface. /// See for more details: /// https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus - bool get windowHasFocus => js_util.callMethod(html.document, 'hasFocus', []); + bool get windowHasFocus => js_util.callMethod(html.document, 'hasFocus', []); void _setupHotRestart() { // This persists across hot restarts to clear stale DOM. diff --git a/lib/web_ui/lib/src/engine/html_image_codec.dart b/lib/web_ui/lib/src/engine/html_image_codec.dart index 28d3107fec24e..c380258d90411 100644 --- a/lib/web_ui/lib/src/engine/html_image_codec.dart +++ b/lib/web_ui/lib/src/engine/html_image_codec.dart @@ -49,7 +49,7 @@ class HtmlCodec implements ui.Codec { imgElement.naturalHeight, ); completer.complete(SingleFrameInfo(image)); - }).catchError((e) { + }).catchError((dynamic e) { // This code path is hit on Chrome 80.0.3987.16 when too many // images are on the page (~1000). // Fallback here is to load using onLoad instead. diff --git a/lib/web_ui/lib/src/engine/render_vertices.dart b/lib/web_ui/lib/src/engine/render_vertices.dart index d39ccd469d169..89849fe463065 100644 --- a/lib/web_ui/lib/src/engine/render_vertices.dart +++ b/lib/web_ui/lib/src/engine/render_vertices.dart @@ -145,7 +145,7 @@ class _WebGlRenderer implements _GlRenderer { gl.bindArrayBuffer(positionsBuffer); gl.bufferData(positions, gl.kStaticDraw); js_util.callMethod( - gl.glContext, 'vertexAttribPointer', [0, 2, gl.kFloat, false, 0, 0]); + gl.glContext, 'vertexAttribPointer', [0, 2, gl.kFloat, false, 0, 0]); gl.enableVertexAttribArray(0); // Setup color buffer. @@ -155,7 +155,7 @@ class _WebGlRenderer implements _GlRenderer { gl.bufferData(vertices.colors, gl.kStaticDraw); js_util.callMethod(gl.glContext, 'vertexAttribPointer', - [1, 4, gl.kUnsignedByte, true, 0, 0]); + [1, 4, gl.kUnsignedByte, true, 0, 0]); gl.enableVertexAttribArray(1); gl.clear(); final int vertexCount = positions.length ~/ 2; @@ -317,7 +317,7 @@ class _GlContext { static Map _programCache; _GlContext.fromOffscreenCanvas(html.OffscreenCanvas canvas) - : glContext = canvas.getContext('webgl2', {'premultipliedAlpha': false}), + : glContext = canvas.getContext('webgl2', {'premultipliedAlpha': false}), isOffscreen = true { _programCache = {}; _canvas = canvas; @@ -325,7 +325,7 @@ class _GlContext { _GlContext.fromCanvas(html.CanvasElement canvas, bool useWebGl1) : glContext = canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2', - {'premultipliedAlpha': false}), + {'premultipliedAlpha': false}), isOffscreen = false { _programCache = {}; _canvas = canvas; @@ -342,7 +342,7 @@ class _GlContext { // Actual size of canvas may be larger than viewport size. Use // source/destination to draw part of the image data. js_util.callMethod(context, 'drawImage', - [_canvas, 0, 0, _widthInPixels, _heightInPixels, + [_canvas, 0, 0, _widthInPixels, _heightInPixels, left, top, _widthInPixels, _heightInPixels]); } @@ -372,10 +372,10 @@ class _GlContext { if (shader == null) { throw Exception(error); } - js_util.callMethod(glContext, 'shaderSource', [shader, source]); - js_util.callMethod(glContext, 'compileShader', [shader]); + js_util.callMethod(glContext, 'shaderSource', [shader, source]); + js_util.callMethod(glContext, 'compileShader', [shader]); bool shaderStatus = js_util - .callMethod(glContext, 'getShaderParameter', [shader, compileStatus]); + .callMethod(glContext, 'getShaderParameter', [shader, compileStatus]); if (!shaderStatus) { throw Exception('Shader compilation failed: ${getShaderInfoLog(shader)}'); } @@ -383,73 +383,73 @@ class _GlContext { } Object createProgram() => - js_util.callMethod(glContext, 'createProgram', const []); + js_util.callMethod(glContext, 'createProgram', const []); void attachShader(Object program, Object shader) { - js_util.callMethod(glContext, 'attachShader', [program, shader]); + js_util.callMethod(glContext, 'attachShader', [program, shader]); } void linkProgram(Object program) { - js_util.callMethod(glContext, 'linkProgram', [program]); + js_util.callMethod(glContext, 'linkProgram', [program]); if (!js_util - .callMethod(glContext, 'getProgramParameter', [program, kLinkStatus])) { + .callMethod(glContext, 'getProgramParameter', [program, kLinkStatus])) { throw Exception(getProgramInfoLog(program)); } } void useProgram(Object program) { - js_util.callMethod(glContext, 'useProgram', [program]); + js_util.callMethod(glContext, 'useProgram', [program]); } Object createBuffer() => - js_util.callMethod(glContext, 'createBuffer', const []); + js_util.callMethod(glContext, 'createBuffer', const []); void bindArrayBuffer(Object buffer) { - js_util.callMethod(glContext, 'bindBuffer', [kArrayBuffer, buffer]); + js_util.callMethod(glContext, 'bindBuffer', [kArrayBuffer, buffer]); } void deleteBuffer(Object buffer) { - js_util.callMethod(glContext, 'deleteBuffer', [buffer]); + js_util.callMethod(glContext, 'deleteBuffer', [buffer]); } void bufferData(TypedData data, dynamic type) { - js_util.callMethod(glContext, 'bufferData', [kArrayBuffer, data, type]); + js_util.callMethod(glContext, 'bufferData', [kArrayBuffer, data, type]); } void enableVertexAttribArray(int index) { - js_util.callMethod(glContext, 'enableVertexAttribArray', [index]); + js_util.callMethod(glContext, 'enableVertexAttribArray', [index]); } /// Clear background. void clear() { - js_util.callMethod(glContext, 'clear', [kColorBufferBit]); + js_util.callMethod(glContext, 'clear', [kColorBufferBit]); } /// Destroys gl context. void dispose() { - js_util.callMethod(_getExtension('WEBGL_lose_context'), 'loseContext', []); + js_util.callMethod(_getExtension('WEBGL_lose_context'), 'loseContext', const []); } void deleteProgram(Object program) { - js_util.callMethod(glContext, 'deleteProgram', [program]); + js_util.callMethod(glContext, 'deleteProgram', [program]); } void deleteShader(Object shader) { - js_util.callMethod(glContext, 'deleteShader', [shader]); + js_util.callMethod(glContext, 'deleteShader', [shader]); } dynamic _getExtension(String extensionName) => - js_util.callMethod(glContext, 'getExtension', [extensionName]); + js_util.callMethod(glContext, 'getExtension', [extensionName]); void drawTriangles(int triangleCount, ui.VertexMode vertexMode) { dynamic mode = _triangleTypeFromMode(vertexMode); - js_util.callMethod(glContext, 'drawArrays', [mode, 0, triangleCount]); + js_util.callMethod(glContext, 'drawArrays', [mode, 0, triangleCount]); } /// Sets affine transformation from normalized device coordinates /// to window coordinates void viewport(double x, double y, double width, double height) { - js_util.callMethod(glContext, 'viewport', [x, y, width, height]); + js_util.callMethod(glContext, 'viewport', [x, y, width, height]); } dynamic _triangleTypeFromMode(ui.VertexMode mode) { @@ -467,10 +467,10 @@ class _GlContext { } Object _createShader(String shaderType) => js_util.callMethod( - glContext, 'createShader', [js_util.getProperty(glContext, shaderType)]); + glContext, 'createShader', [js_util.getProperty(glContext, shaderType)]); /// Error state of gl context. - dynamic get error => js_util.callMethod(glContext, 'getError', const []); + dynamic get error => js_util.callMethod(glContext, 'getError', const []); /// Shader compiler error, if this returns [kFalse], to get details use /// [getShaderInfoLog]. @@ -508,37 +508,37 @@ class _GlContext { /// Returns reference to uniform in program. Object getUniformLocation(Object program, String uniformName) { return js_util - .callMethod(glContext, 'getUniformLocation', [program, uniformName]); + .callMethod(glContext, 'getUniformLocation', [program, uniformName]); } /// Sets vec2 uniform values. void setUniform2f(Object uniform, double value1, double value2) { return js_util - .callMethod(glContext, 'uniform2f', [uniform, value1, value2]); + .callMethod(glContext, 'uniform2f', [uniform, value1, value2]); } /// Sets vec4 uniform values. void setUniform4f(Object uniform, double value1, double value2, double value3, double value4) { return js_util.callMethod( - glContext, 'uniform4f', [uniform, value1, value2, value3, value4]); + glContext, 'uniform4f', [uniform, value1, value2, value3, value4]); } /// Sets mat4 uniform values. void setUniformMatrix4fv(Object uniform, bool transpose, Float64List value) { return js_util.callMethod( - glContext, 'uniformMatrix4fv', [uniform, transpose, value]); + glContext, 'uniformMatrix4fv', [uniform, transpose, value]); } /// Shader compile error log. dynamic getShaderInfoLog(Object glShader) { - return js_util.callMethod(glContext, 'getShaderInfoLog', [glShader]); + return js_util.callMethod(glContext, 'getShaderInfoLog', [glShader]); } /// Errors that occurred during failed linking or validation of program /// objects. Typically called after [linkProgram]. String getProgramInfoLog(Object glProgram) { - return js_util.callMethod(glContext, 'getProgramInfoLog', [glProgram]); + return js_util.callMethod(glContext, 'getProgramInfoLog', [glProgram]); } int get drawingBufferWidth => @@ -555,7 +555,7 @@ class _GlContext { final Uint8List pixels = Uint8List(bufferWidth * bufferHeight * kBytesPerPixel); js_util.callMethod(glContext, 'readPixels', - [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); + [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); return html.ImageData( Uint8ClampedList.fromList(pixels), bufferWidth, bufferHeight); } else { @@ -565,7 +565,7 @@ class _GlContext { final Uint8ClampedList pixels = Uint8ClampedList(bufferWidth * bufferHeight * kBytesPerPixel); js_util.callMethod(glContext, 'readPixels', - [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); + [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); return html.ImageData(pixels, bufferWidth, bufferHeight); } } diff --git a/lib/web_ui/lib/src/engine/shadow.dart b/lib/web_ui/lib/src/engine/shadow.dart index 62806e029b518..cd6abe34f9ace 100644 --- a/lib/web_ui/lib/src/engine/shadow.dart +++ b/lib/web_ui/lib/src/engine/shadow.dart @@ -45,7 +45,7 @@ const double kLightOffsetY = -400.0; /// This is not a complete physical model. For example, this does not take into /// account the size of the shape (this function doesn't even take the shape as /// a parameter). It's just a good enough approximation. -ui.Offset computeShadowOffset(elevation) { +ui.Offset computeShadowOffset(double elevation) { if (elevation == 0.0) { return ui.Offset.zero; } diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index b2e317d27f5ea..38655a528d861 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -924,8 +924,8 @@ class MeasurementResult { @required this.alphabeticBaseline, @required this.ideographicBaseline, @required this.lines, - @required textAlign, - @required textDirection, + @required ui.TextAlign textAlign, + @required ui.TextDirection textDirection, }) : assert(constraintWidth != null), assert(isSingleLine != null), assert(width != null), diff --git a/lib/web_ui/lib/src/ui/canvas.dart b/lib/web_ui/lib/src/ui/canvas.dart index 9359f0487e00a..c27e513fc4b08 100644 --- a/lib/web_ui/lib/src/ui/canvas.dart +++ b/lib/web_ui/lib/src/ui/canvas.dart @@ -963,7 +963,9 @@ class Canvas { } void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) { - if (vertices == null) return; + if (vertices == null) { + return; + } //assert(vertices != null); // vertices is checked on the engine side assert(paint != null); assert(blendMode != null); diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index a55779537e150..18aec485487ff 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1105,8 +1105,12 @@ class TextRange { @override bool operator ==(dynamic other) { - if (identical(this, other)) return true; - if (other is! TextRange) return false; + if (identical(this, other)) { + return true; + } + if (other is! TextRange) { + return false; + } final TextRange typedOther = other; return typedOther.start == start && typedOther.end == end; } diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index d58cc5f85908c..58c2d672a13eb 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -485,8 +485,12 @@ class Locale { String _rawToString(String separator) { final StringBuffer out = StringBuffer(languageCode); - if (scriptCode != null) out.write('$separator$scriptCode'); - if (_countryCode != null) out.write('$separator$countryCode'); + if (scriptCode != null) { + out.write('$separator$scriptCode'); + } + if (_countryCode != null) { + out.write('$separator$countryCode'); + } return out.toString(); } } diff --git a/lib/web_ui/test/engine/history_test.dart b/lib/web_ui/test/engine/history_test.dart index 01e67c388e92d..c7c31b2cbd124 100644 --- a/lib/web_ui/test/engine/history_test.dart +++ b/lib/web_ui/test/engine/history_test.dart @@ -171,7 +171,7 @@ void main() { await _strategy.simulateUserTypingUrl('/page3'); // This delay is necessary to wait for [BrowserHistory] because it // performs a `back` operation which results in a new event loop. - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); // 1. The engine sends a `pushRoute` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); @@ -212,7 +212,7 @@ void main() { await _strategy.simulateUserTypingUrl('/unknown'); // This delay is necessary to wait for [BrowserHistory] because it // performs a `back` operation which results in a new event loop. - await Future.delayed(Duration.zero); + await Future.delayed(Duration.zero); // 1. The engine sends a `pushRoute` platform message. expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/navigation'); diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index a23d559bb54b4..caa345b238534 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -1970,7 +1970,7 @@ class _TouchEventContext extends _BasicEventContext with _MultiPointerEventMixin double clientX, double clientY, }) { - return html.Touch({ + return html.Touch({ 'identifier': identifier, 'clientX': clientX, 'clientY': clientY, @@ -1979,7 +1979,7 @@ class _TouchEventContext extends _BasicEventContext with _MultiPointerEventMixin } html.TouchEvent _createTouchEvent(String eventType, List<_TouchDetails> touches) { - return html.TouchEvent(eventType, { + return html.TouchEvent(eventType, { 'changedTouches': touches.map( (_TouchDetails details) => _createTouch( @@ -2138,7 +2138,7 @@ class _PointerEventContext extends _BasicEventContext with _ButtonedEventMixin i } html.Event _downWithFullDetails({double clientX, double clientY, int button, int buttons, int pointer, String pointerType}) { - return html.PointerEvent('pointerdown', { + return html.PointerEvent('pointerdown', { 'pointerId': pointer, 'button': button, 'buttons': buttons, @@ -2173,7 +2173,7 @@ class _PointerEventContext extends _BasicEventContext with _ButtonedEventMixin i } html.Event _moveWithFullDetails({double clientX, double clientY, int button, int buttons, int pointer, String pointerType}) { - return html.PointerEvent('pointermove', { + return html.PointerEvent('pointermove', { 'pointerId': pointer, 'button': button, 'buttons': buttons, @@ -2206,7 +2206,7 @@ class _PointerEventContext extends _BasicEventContext with _ButtonedEventMixin i } html.Event _upWithFullDetails({double clientX, double clientY, int button, int pointer, String pointerType}) { - return html.PointerEvent('pointerup', { + return html.PointerEvent('pointerup', { 'pointerId': pointer, 'button': button, 'buttons': 0, @@ -2218,7 +2218,7 @@ class _PointerEventContext extends _BasicEventContext with _ButtonedEventMixin i @override List multiTouchCancel(List<_TouchDetails> touches) { - return touches.map((_TouchDetails details) => html.PointerEvent('pointercancel', { + return touches.map((_TouchDetails details) => html.PointerEvent('pointercancel', { 'pointerId': details.pointer, 'button': 0, 'buttons': 0, diff --git a/lib/web_ui/test/engine/recording_canvas_test.dart b/lib/web_ui/test/engine/recording_canvas_test.dart index 6e375bfc9ac6d..2757ba96fd4a4 100644 --- a/lib/web_ui/test/engine/recording_canvas_test.dart +++ b/lib/web_ui/test/engine/recording_canvas_test.dart @@ -27,7 +27,7 @@ void main() { underTest.drawDRRect(rrect, rrect.deflate(1), somePaint); underTest.apply(mockCanvas); - _expectDrawCall(mockCanvas, { + _expectDrawCall(mockCanvas, { 'outer': rrect, 'inner': rrect.deflate(1), 'paint': somePaint.paintData, @@ -75,7 +75,7 @@ void main() { underTest.apply(mockCanvas); // Expect to draw, even when inner has negative radii (which get ignored by canvas) - _expectDrawCall(mockCanvas, { + _expectDrawCall(mockCanvas, { 'outer': outer, 'inner': inner, 'paint': somePaint.paintData, @@ -91,7 +91,7 @@ void main() { underTest.drawDRRect(outer, inner, somePaint); underTest.apply(mockCanvas); - _expectDrawCall(mockCanvas, { + _expectDrawCall(mockCanvas, { 'outer': outer, 'inner': inner, 'paint': somePaint.paintData, diff --git a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart index ee05bd37b95aa..694274bfe516e 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart @@ -135,6 +135,6 @@ HtmlImage createTestImage() { ctx.fillRect(66, 0, 33, 50); ctx.fill(); html.ImageElement imageElement = html.ImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); return HtmlImage(imageElement, width, height); } diff --git a/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart index 81f3fc8b8eadf..316b6b3467fde 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart @@ -101,6 +101,6 @@ engine.HtmlImage createTestImage({int width = 200, int height = 150}) { ctx.fillRect(2 * width / 3, 0, width / 3, height); ctx.fill(); html.ImageElement imageElement = html.ImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); return engine.HtmlImage(imageElement, width, height); } diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart index 1e86fefdb4cbb..762c7b810fc5a 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart @@ -326,6 +326,6 @@ HtmlImage createTestImage({int width = 100, int height = 50}) { ctx.fillRect(66, 0, 33, 50); ctx.fill(); html.ImageElement imageElement = html.ImageElement(); - imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); + imageElement.src = js_util.callMethod(canvas, 'toDataURL', []); return HtmlImage(imageElement, width, height); } From 70f5a07fe6ac04f35ff81c7b4806f35649e2043d Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 30 Mar 2020 00:20:54 -0700 Subject: [PATCH 177/386] Document flutter::DartVMData. (#17392) --- runtime/dart_vm_data.h | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/runtime/dart_vm_data.h b/runtime/dart_vm_data.h index 0f054bf55f3d6..49ddd316899a4 100644 --- a/runtime/dart_vm_data.h +++ b/runtime/dart_vm_data.h @@ -10,19 +10,65 @@ namespace flutter { +//------------------------------------------------------------------------------ +/// @brief Provides thread-safe access to data that is necessary to +/// bootstrap a new Dart VM instance. All snapshots referenced by +/// this object are read-only. +/// class DartVMData { public: + //---------------------------------------------------------------------------- + /// @brief Creates a new instance of `DartVMData`. Both the VM and + /// isolate snapshot members are optional and may be `nullptr`. If + /// `nullptr`, the snapshot resolvers present in the settings + /// object are used to infer the snapshots. If the snapshots + /// cannot be inferred from the settings object, this method + /// return `nullptr`. + /// + /// @param[in] settings The settings used to infer the VM and + /// isolate snapshots if they are not provided + /// directly. + /// @param[in] vm_snapshot The VM snapshot or `nullptr`. + /// @param[in] isolate_snapshot The isolate snapshot or `nullptr`. + /// + /// @return A new instance of VM data that can be used to bootstrap a Dart + /// VM. `nullptr` if the snapshots are not provided and cannot be + /// inferred from the settings object. + /// static std::shared_ptr Create( Settings settings, fml::RefPtr vm_snapshot, fml::RefPtr isolate_snapshot); + //---------------------------------------------------------------------------- + /// @brief Collect the DartVMData instance. + /// ~DartVMData(); + //---------------------------------------------------------------------------- + /// @brief The settings object from which the Dart snapshots were + /// inferred. + /// + /// @return The settings. + /// const Settings& GetSettings() const; + //---------------------------------------------------------------------------- + /// @brief Gets the VM snapshot. This can be in the call to bootstrap + /// the Dart VM via `Dart_Initialize`. + /// + /// @return The VM snapshot. + /// const DartSnapshot& GetVMSnapshot() const; + //---------------------------------------------------------------------------- + /// @brief Get the isolate snapshot necessary to launch isolates in the + /// Dart VM. The Dart VM instance in which these isolates are + /// launched must be the same as the VM created using snapshot + /// accessed via `GetVMSnapshot`. + /// + /// @return The isolate snapshot. + /// fml::RefPtr GetIsolateSnapshot() const; private: From ac89bc2b2a6b091d57d4459a71e99a0d79e8d3f1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 30 Mar 2020 08:25:50 -0400 Subject: [PATCH 178/386] Roll src/third_party/skia 7345d88277c4..5b11a58c6297 (18 commits) (#17387) https://skia.googlesource.com/skia.git/+log/7345d88277c4..5b11a58c6297 git log 7345d88277c4..5b11a58c6297 --date=short --first-parent --format='%ad %ae %s' 2020-03-28 mtklein@google.com skvm::Builder friendliness improvements 2020-03-28 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update Go Deps 2020-03-27 bungeman@google.com Fix gcc 9 warnings. 2020-03-27 bsalomon@google.com Rename SkColorTypeComponentFlags to SkColorChannelFlags. 2020-03-27 reed@google.com all colorfilters but RT converted to skvm 2020-03-27 egdaniel@google.com Add GrD3DPipelineState class and setup vertex input information. 2020-03-27 reed@google.com onProgram for SkGaussianColorFilter, plus gm and bench 2020-03-27 csmartdalton@google.com Rename TessellatingPathRendererTests.cpp -> "Triangulating" 2020-03-27 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 02e15b249b12..3ca41df3d3ba (10 commits) 2020-03-27 egdaniel@google.com Remove some unused 3 byte vertex attrib types. 2020-03-27 robertphillips@google.com Make the distanceFieldAdjustTable a static singleton 2020-03-27 jvanverth@google.com Minor fixes for D3D build. 2020-03-27 bungeman@google.com Fix gcc9 warning around size of memset. 2020-03-27 csmartdalton@google.com Rename GrGpuTessellationPathRenderer to GrTessellationPathRenderer 2020-03-27 nifong@google.com Remove deprecated functions 2020-03-27 reed@google.com onProgram for overdraw-colorfilter 2020-03-27 bsalomon@google.com Remove texture swizzle from GrSurfaceProxy. 2020-03-27 bsalomon@google.com Fix TODO SkImage_GpuBase to check SkColorInfo and not a fake SkImageInfo Created with: gclient setdep -r src/third_party/skia@5b11a58c6297 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC robertphillips@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: robertphillips@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 5161795463030..ef2a094ee002f 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '7345d88277c4796ff9875aa7f1be4810b8461c2b', + 'skia_revision': '5b11a58c6297c44d5fae74ccd2b6c6fad2d60c5f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index f509945478a7b..97d11b8c26ce7 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: b0ea5feef6396c23f492abb5fab4cf58 +Signature: 6cfc18d48cb45c407bc43d221507b731 UNUSED LICENSES: @@ -3913,6 +3913,8 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DGpu.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DOpsRenderPass.h +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipelineState.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DPipelineState.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DRenderTarget.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DRenderTarget.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DResourceProvider.cpp @@ -5072,12 +5074,12 @@ FILE: ../../../third_party/skia/src/gpu/ccpr/GrStencilAtlasOp.h FILE: ../../../third_party/skia/src/gpu/effects/GrComposeLerpEffect.fp FILE: ../../../third_party/skia/src/gpu/effects/generated/GrComposeLerpEffect.cpp FILE: ../../../third_party/skia/src/gpu/effects/generated/GrComposeLerpEffect.h -FILE: ../../../third_party/skia/src/gpu/tessellate/GrGpuTessellationPathRenderer.cpp -FILE: ../../../third_party/skia/src/gpu/tessellate/GrGpuTessellationPathRenderer.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrStencilPathShader.cpp FILE: ../../../third_party/skia/src/gpu/tessellate/GrStencilPathShader.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrTessellatePathOp.cpp FILE: ../../../third_party/skia/src/gpu/tessellate/GrTessellatePathOp.h +FILE: ../../../third_party/skia/src/gpu/tessellate/GrTessellationPathRenderer.cpp +FILE: ../../../third_party/skia/src/gpu/tessellate/GrTessellationPathRenderer.h FILE: ../../../third_party/skia/src/pdf/SkPDFGraphicStackState.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFGraphicStackState.h FILE: ../../../third_party/skia/src/pdf/SkPDFType1Font.cpp @@ -5634,6 +5636,7 @@ FILE: ../../../third_party/skia/modules/sksg/src/SkSGDashEffect.cpp FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_coretext.cpp FILE: ../../../third_party/skia/samplecode/Sample3D.cpp FILE: ../../../third_party/skia/src/core/SkCanvasMatrix.h +FILE: ../../../third_party/skia/src/core/SkColorFilterPriv.h FILE: ../../../third_party/skia/src/core/SkCompressedDataUtils.cpp FILE: ../../../third_party/skia/src/core/SkCompressedDataUtils.h FILE: ../../../third_party/skia/src/core/SkM44.cpp From 7338f5091c33ae014ce3da5719aef564e2b8b09d Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Mon, 30 Mar 2020 11:55:46 -0700 Subject: [PATCH 179/386] Readme change to trigger builds (#17398) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0c4190c9aba64..eb16aa51bb3e0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ Flutter Engine ============== + [![Build Status - Cirrus][]][Build status] Flutter is Google's mobile app SDK for crafting high-quality native interfaces From c779894d089554620134408b3cb6ea515489dc14 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 30 Mar 2020 12:54:06 -0700 Subject: [PATCH 180/386] [web] Fix canvas z-index leaking across repaints when element is reused. (#17378) * Fix z-index leak. Add test for canvas reuse * add regression test * update golden locks * fix analysis errors --- lib/web_ui/dev/goldens_lock.yaml | 2 +- lib/web_ui/lib/src/engine/canvas_pool.dart | 4 + .../engine/canvas_reuse_test.dart | 98 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart diff --git a/lib/web_ui/dev/goldens_lock.yaml b/lib/web_ui/dev/goldens_lock.yaml index f32239505dc7a..137070e356b29 100644 --- a/lib/web_ui/dev/goldens_lock.yaml +++ b/lib/web_ui/dev/goldens_lock.yaml @@ -1,2 +1,2 @@ repository: https://github.com/flutter/goldens.git -revision: ae6003206eb721137c20cd56d8d1d8e2a76d6dd1 +revision: 5ae87c98ad4abf882a2d312e4c4f75d7ad6d845b diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index 3702513e261a2..b97ffede6f0ae 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -79,6 +79,10 @@ class _CanvasPool extends _SaveStackTracking { bool requiresClearRect = 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'); requiresClearRect = true; } else { // Compute the final CSS canvas size given the actual pixel count we diff --git a/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart new file mode 100644 index 0000000000000..e92f55287a252 --- /dev/null +++ b/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart @@ -0,0 +1,98 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +import 'dart:html' as html; + +import 'package:ui/ui.dart' hide TextStyle; +import 'package:ui/src/engine.dart'; +import 'package:test/test.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +void main() async { + const double screenWidth = 600.0; + const double screenHeight = 800.0; + const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); + final Paint testPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFFFF00FF); + + setUp(() async { + debugEmulateFlutterTesterEnvironment = true; + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + // Regression test for https://github.com/flutter/flutter/issues/51514 + test('Canvas is reused and z-index doesn\'t leak across paints', () async { + final EngineCanvas engineCanvas = BitmapCanvas(screenRect); + const Rect region = Rect.fromLTWH(0, 0, 500, 500); + + // Draw first frame into engine canvas. + final RecordingCanvas rc = + RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); + final Path path = Path() + ..moveTo(3, 0) + ..lineTo(100, 97); + rc.drawPath(path, testPaint); + rc.apply(engineCanvas); + engineCanvas.endOfPaint(); + + html.Element sceneElement = html.Element.tag('flt-scene'); + sceneElement.append(engineCanvas.rootElement); + html.document.body.append(sceneElement); + + final html.CanvasElement canvas = html.document.querySelector('canvas'); + // ! Since canvas is first element, it should have zIndex = -1 for correct + // paint order. + expect(canvas.style.zIndex , '-1'); + + // Add id to canvas element to test for reuse. + const String kTestId = 'test-id-5698'; + canvas.id = kTestId; + + sceneElement.remove(); + // Clear so resources are marked for reuse. + + engineCanvas.clear(); + + // Now paint a second scene to same [BitmapCanvas] but paint an image + // before the path to move canvas element into second position. + final RecordingCanvas rc2 = + RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); + final Path path2 = Path() + ..moveTo(3, 0) + ..quadraticBezierTo(100, 0, 100, 100); + rc2.drawImage(_createRealTestImage(), Offset(0, 0), Paint()); + rc2.drawPath(path2, testPaint); + rc2.apply(engineCanvas); + + sceneElement = html.Element.tag('flt-scene'); + sceneElement.append(engineCanvas.rootElement); + html.document.body.append(sceneElement); + + final html.CanvasElement canvas2 = html.document.querySelector('canvas'); + // ZIndex should have been cleared since we have image element preceding + // canvas. + expect(canvas.style.zIndex != '-1', true); + expect(canvas2.id, kTestId); + await matchGoldenFile('bitmap_canvas_reuse_zindex.png', region: region); + }); +} + +const String _base64Encoded20x20TestImage = 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' + 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' + 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; + +HtmlImage _createRealTestImage() { + return HtmlImage( + html.ImageElement() + ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', + 20, + 20, + ); +} From dffcafc21349f75387aca1719381c9518aa9fc4f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 30 Mar 2020 16:24:04 -0400 Subject: [PATCH 181/386] Roll src/third_party/skia 5b11a58c6297..e9bc857b39c2 (5 commits) (#17395) https://skia.googlesource.com/skia.git/+log/5b11a58c6297..e9bc857b39c2 git log 5b11a58c6297..e9bc857b39c2 --date=short --first-parent --format='%ad %ae %s' 2020-03-30 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/angle2 f2bee3043a5e..1b2dd6f92a6b (18 commits) 2020-03-30 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ../src 2cb42d2f8146..af1924a67aab (483 commits) 2020-03-30 skia-autoroll@skia-public.iam.gserviceaccount.com Roll third_party/externals/swiftshader 3ca41df3d3ba..1c29477aa06e (9 commits) 2020-03-29 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update SKP version 2020-03-29 skia-recreate-skps@skia-swarming-bots.iam.gserviceaccount.com Update Go Deps Created with: gclient setdep -r src/third_party/skia@e9bc857b39c2 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC michaelludwig@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: michaelludwig@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index ef2a094ee002f..ce228b640b673 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '5b11a58c6297c44d5fae74ccd2b6c6fad2d60c5f', + 'skia_revision': 'e9bc857b39c22878fd2f46284d83f22fe45540d9', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 97d11b8c26ce7..66c7873e699a2 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6cfc18d48cb45c407bc43d221507b731 +Signature: 3f2ce43aca5125ec5f543abede6a3203 UNUSED LICENSES: From 915e63ecb741238aed759ec057c79af1a292cae3 Mon Sep 17 00:00:00 2001 From: wqyfavor Date: Tue, 31 Mar 2020 05:26:03 +0800 Subject: [PATCH 182/386] Fix FlutterView's _delegate is not declared as ivar but a global variable. (#17206) --- shell/platform/darwin/ios/framework/Source/FlutterView.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterView.mm b/shell/platform/darwin/ios/framework/Source/FlutterView.mm index ed2d1f02b7127..783eafa336f9b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterView.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterView.mm @@ -17,9 +17,9 @@ #include "flutter/shell/platform/darwin/ios/ios_surface_software.h" #include "third_party/skia/include/utils/mac/SkCGUtils.h" -@implementation FlutterView - -id _delegate; +@implementation FlutterView { + id _delegate; +} - (instancetype)init { @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate" From 141d2ea050d9847da2f1ff412cd7511148f64cd6 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Mon, 30 Mar 2020 14:26:10 -0700 Subject: [PATCH 183/386] Use the private presentation window type for platform view windows (#17385) --- .../io/flutter/plugin/platform/SingleViewPresentation.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java index 9bda1295b9b88..63bf92555c410 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java @@ -125,6 +125,9 @@ public SingleViewPresentation( .setFlags( WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + getWindow().setType(WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); + } } /** From 193bb355d93933eba99325c5cff7620ff35faaea Mon Sep 17 00:00:00 2001 From: liyuqian Date: Mon, 30 Mar 2020 14:31:03 -0700 Subject: [PATCH 184/386] Disable OnServiceProtocolGetSkSLsWorks in Fuchsia (#17361) --- shell/common/shell_unittests.cc | 3 +-- testing/fuchsia/run_tests.sh | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index e6f9a1f12cbdd..21787017d2d02 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1132,8 +1132,7 @@ TEST_F(ShellTest, CanDecompressImageFromAsset) { DestroyShell(std::move(shell)); } -// TODO(53399): Re-enable once it passes on Fuchsia. -TEST_F(ShellTest, DISABLED_OnServiceProtocolGetSkSLsWorks) { +TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) { // Create 2 dummpy SkSL cache file IE (base32 encoding of A), II (base32 // encoding of B) with content x and y. fml::ScopedTemporaryDirectory temp_dir; diff --git a/testing/fuchsia/run_tests.sh b/testing/fuchsia/run_tests.sh index 8bbd5be91b990..90eb36ab8e486 100755 --- a/testing/fuchsia/run_tests.sh +++ b/testing/fuchsia/run_tests.sh @@ -71,8 +71,10 @@ done -f runtime_tests-0.far \ -t runtime_tests +# TODO(https://github.com/flutter/flutter/issues/53399): Re-enable +# OnServiceProtocolGetSkSLsWorks once it passes on Fuchsia. ./fuchsia_ctl -d $device_name test \ -f shell_tests-0.far \ -t shell_tests \ - -a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*" + -a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks" From 113ebf963fc15c63586e1311e7b83ddd94d5e5a8 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 30 Mar 2020 15:22:47 -0700 Subject: [PATCH 185/386] Roll src/third_party/dart 1402e8e1a42d.e21c4353006d (95 commits) (#17386) git log 1402e8e1a42d..e21c4353006d665e7c4378413ad4d237f18437e0 --date=short --first-parent --format='%ad %ae %s' 2020-03-30 jsimmons@google.com [dart:io] Fuchsia version of SocketBase::AvailableDatagram 2020-03-30 jwren@google.com Fix the dart help test output by implementing printUsage() in the test command 2020-03-30 sstrickl@google.com [vm/aot] Drop some Function objects not needed at runtime. 2020-03-30 mit@google.com Roll pub to latest version 2020-03-30 scheglov@google.com Document differences in inheritance tests between CFE and analyzer. 2020-03-30 scheglov@google.com Revert "Fix the result type of binary expression with the left TypeParameter extends num type." 2020-03-30 johnniwinther@google.com [cfe] Handle member signatures from dill in class hierarchy builder 2020-03-30 eernst@google.com [cfe] Add missing .next in endImplicitCreationExpression 2020-03-30 jensj@google.com [CFE] Add api to get language version for uri 2020-03-30 johnniwinther@google.com [cfe] Don't include member signature in id testing by default. 2020-03-30 johnniwinther@google.com [kernel] Make verbose qualified names conditional in toStringInternal 2020-03-30 devoncarew@google.com [dartdev] test the dartdev analyze --fatal-warnings option 2020-03-30 brianwilkerson@google.com Enable omit_local_variable_types in analyzer_plugin 2020-03-30 brianwilkerson@google.com Enable omit_loval_variable_types in analysis_server_client 2020-03-30 brianwilkerson@google.com Enable omit_local_variable_types in analysis_server 2020-03-30 keertip@google.com Add guard code for exceptions from ResourceProvider when reading contents. 2020-03-29 scheglov@google.com Fix the result type of binary expression with the left TypeParameter extends num type. 2020-03-28 scheglov@google.com Fix for creating a setter when the target is in a part without a library. 2020-03-28 brianwilkerson@google.com Remove the need for a --corpus flag from completion_metrics 2020-03-28 brianwilkerson@google.com Remove the need for a --corpus flag from relevance_metrics 2020-03-28 brianwilkerson@google.com Remove an unused variable in AstBuilder 2020-03-28 brianwilkerson@google.com Sort non-generated files in analyis_server and add a test to ensure they stay that way 2020-03-28 scheglov@google.com Add LibraryElement.languageVersionMajor/Minor 2020-03-28 scheglov@google.com Report exceptions with the file content for getResult(). 2020-03-28 scheglov@google.com Fix crash in index when the invoked element is multiply defined. 2020-03-28 eernst@google.com Add implementation of several parts of non-function type aliases 2020-03-28 johnniwinther@google.com [cfe] Groups standard tests in update_expectations.dart 2020-03-28 sra@google.com [dart2js] Specialize is- and as- tests for nullable types 2020-03-28 rmacnak@google.com [vm] Do not allocate when evaluating arguments to StorePointer. 2020-03-28 rmacnak@google.com [vm] Fix some data races with profiler samples. 2020-03-28 alexmarkov@google.com Revert "[analyzer] Remove deprecated language version API" 2020-03-27 nshahan@google.com [ddc] Cleanup cast in dart:_runtime lib 2020-03-27 brianwilkerson@google.com New relevance values for UriContributor 2020-03-27 brianwilkerson@google.com New relevance values for KeywordContributor 2020-03-27 brianwilkerson@google.com New relevance values for VariableNameContributor 2020-03-27 paulberry@google.com Migration: don't use union edges for field formal parameters 2020-03-27 rmacnak@google.com [vm] Fix memory leak in profiler when some samples are in native functions without a dynamic symbol. 2020-03-27 bkonyi@google.com [ package:dds ] Fixed smoke_test.dart which was unable to launch smoke.dart due to use of a relative path. 2020-03-27 zichangguo@google.com [dart:io] Fix parsing the response of the HTTP connect method 2020-03-27 mfairhurst@google.com [nnbd_migration] Add confirm dialog to "apply migration" button 2020-03-27 rmacnak@google.com [test] Update use of Null as a bottom type to Never. 2020-03-27 eernst@google.com Added new tests. 2020-03-27 alexmarkov@google.com [vm/nnbd/aot/tfa] Support NNBD strong mode in type flow analysis 2020-03-27 mfairhurst@google.com [analyzer] Remove deprecated language version API 2020-03-27 brianwilkerson@google.com Update relevance computations in LocalReferenceContributor 2020-03-27 brianwilkerson@google.com Update relevance values for LocalLibraryContributor and ImportedReferenceContributor 2020-03-27 sigurdm@google.com Bump pub version 2020-03-27 sigmund@google.com [dart2js] Update test-matrix to specify the libraries.json to dart2js batch runner 2020-03-27 jcollins@google.com Update dartdoc to 0.30.3. 2020-03-27 alexmarkov@google.com [vm/nnbd] Fix marking static fields as late 2020-03-27 scheglov@google.com Fix for completion in formal parameters of GenericFunctionType. 2020-03-27 mfairhurst@google.com [nnbd_migration] Add a "rerun from sources" button. 2020-03-27 mfairhurst@google.com Revert "[analyzer] Copy result of allSupertypes for now." 2020-03-27 scheglov@google.com Merge types during instance member inference. 2020-03-27 scheglov@google.com Catch linking exceptions and include the cycle file contents. 2020-03-27 johnniwinther@google.com [cfe] Handle throw in return type inference 2020-03-27 alexmarkov@google.com [vm/aot/tfa] Fix tracking of parameters in async_op closures 2020-03-27 zichangguo@google.com Reland "[dart:io] Fix hanging on zero-length datagram" 2020-03-27 athom@google.com [infra] Update checked-in SDKs to 2.8.0-dev.18.0 2020-03-27 johnniwinther@google.com [cfe] Add tool for updating all id-tests and expectation tests 2020-03-27 athom@google.com [homebrew] Add support for the beta channel formula 2020-03-27 brianwilkerson@google.com Sort files in analysis_server_client and analyzer_plugin and add tests to keep them that way 2020-03-27 askesc@google.com [benchmark] Benchmark for UTF-8 decoding with typical data. 2020-03-27 jensj@google.com [parser] Change recovery for constructor; report errors in bad constructors 2020-03-27 johnniwinther@google.com [cfe] Use legacy erasure before argument inference 2020-03-27 jensj@google.com [kernel] Polish toString implementation on AST nodes 2020-03-27 sra@google.com [dart2js] Print non-minified init object as multiline 2020-03-27 karlklose@google.com [infra] Handle test steps with no tested configuration 2020-03-27 zichangguo@google.com [dart:io] remove available at _NativeSocket::Read 2020-03-27 nbosch@google.com Remove a stale TODO comment 2020-03-27 rnystrom@google.com Add other breaking changes to CHANGELOG. 2020-03-27 liama@google.com [nnbd/test] Fix tests that are failing in strong mode 2020-03-26 rnystrom@google.com Fix Markdown link in CHANGELOG and remove duplicate entry. 2020-03-26 brianwilkerson@google.com Compute new relevance in LocalConstructorContributor 2020-03-26 kevmoo@google.com DEPS: remove references to pkg:package_resolver 2020-03-26 jcollins@google.com Add a way for analysis_server to open communication via a IsolateChannel. 2020-03-26 scheglov@google.com Use NNBD_TOP_MERGE to combine signatures of the inherited members. 2020-03-26 nshahan@google.com [ddc] Update types in JSArray operations 2020-03-26 srawlins@google.com Migrator: Add an auth token for the preview site 2020-03-26 dacoharkes@google.com [vm] Add Reachability Fence Instruction 2020-03-26 sra@google.com [benchmark] Make BigIntPrintParse be NNBD-agnostic 2020-03-26 rnystrom@google.com Bump the SDK constraint on dart_internal to permit Dart 2.8.0. 2020-03-26 kevmoo@google.com Update changelog with dart2js --package-root removal 2020-03-26 alexmarkov@google.com [vm/corelib] Specify types explicitly in convert_patch.dart 2020-03-26 scheglov@google.com Update checkForWrongTypeParameterVarianceInMethod(), fix NPE. 2020-03-26 mfairhurst@google.com [analyzer] Copy result of allSupertypes for now. 2020-03-26 scheglov@google.com Add more data to debug crash in buildExportNamespace(). 2020-03-26 scheglov@google.com Fix for crash when FieldFormalParameter in a local function has annotations. 2020-03-26 bkonyi@google.com [ VM / DartDev ] Disable DartDev for the 2.8 release 2020-03-26 kevmoo@google.com Remove unused package-root option and configuration from dart2js 2020-03-26 srujzs@google.com [dart:html] Add nullability to EventTarget conversion 2020-03-26 kevmoo@google.com DEPS: Bring in latest pub 2020-03-26 dacoharkes@google.com [vm/ffi] Mark Struct as entrypoint 2020-03-26 jensj@google.com [analyzer] Sort entries in error.dart and syntactic_errors.dart 2020-03-26 karlklose@google.com [infra] pkg/smith: custom test runner steps are test steps --- DEPS | 12 ++++-------- ci/licenses_golden/licenses_third_party | 12 +++++++++++- lib/ui/hooks.dart | 4 ++-- .../scenario_app/lib/src/animated_color_square.dart | 2 +- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/DEPS b/DEPS index ce228b640b673..673a911e306c6 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '1402e8e1a42d6be6ea370346677cd57093d4be03', + 'dart_revision': 'e21c4353006d665e7c4378413ad4d237f18437e0', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -72,12 +72,11 @@ vars = { 'dart_oauth2_tag': '1.2.1', 'dart_observatory_pub_packages_rev': '0894122173b0f98eb08863a7712e78407d4477bc', 'dart_package_config_tag': 'v1.9.2', - 'dart_package_resolver_tag': '1.0.10', 'dart_path_tag': '1.6.2', 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': 'ab109723d9eb782c2ea4cce68c044da40403a652', + 'dart_pub_rev': '8777719d1a8261b42a81b3b4025b7d695fe0c683', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', @@ -228,7 +227,7 @@ deps = { Var('dart_git') + '/dart2js_info.git' + '@' + Var('dart_dart2js_info_tag'), 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@v0.30.2', + Var('dart_git') + '/dartdoc.git@v0.30.3', 'src/third_party/dart/third_party/pkg/ffi': Var('dart_git') + '/ffi.git' + '@' + Var('dart_ffi_tag'), @@ -377,11 +376,8 @@ deps = { 'src/third_party/dart/third_party/pkg_tested/package_config': Var('dart_git') + '/package_config.git' + '@' + Var('dart_package_config_tag'), - 'src/third_party/dart/third_party/pkg_tested/package_resolver': - Var('dart_git') + '/package_resolver.git' + '@' + Var('dart_package_resolver_tag'), - 'src/third_party/dart/tools/sdks': - {'packages': [{'version': 'version:2.8.0-dev.0.0', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, + {'packages': [{'version': 'version:2.8.0-dev.18.0', 'package': 'dart/dart-sdk/${{platform}}'}], 'dep_type': 'cipd'}, # WARNING: end of dart dependencies list that is cleaned up automatically - see create_updated_flutter_deps.py. diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 7feaa9a2d08d9..34b5fae936912 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: da1bde54103c07da0fd99fa9e639a77b +Signature: f410ed851c283cb74c817135c16276f4 UNUSED LICENSES: @@ -7421,6 +7421,12 @@ FILE: ../../../third_party/dart/benchmarks/IsolateJson/dart/sample.json FILE: ../../../third_party/dart/benchmarks/IsolateSpawn/dart/helloworld.dart FILE: ../../../third_party/dart/benchmarks/IsolateSpawnMemory/dart/helloworld.dart FILE: ../../../third_party/dart/benchmarks/SoundSplayTreeSieve/dart/iterable.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/datext_latin1_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/entext_ascii_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/netext_3_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/rutext_2_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/sktext_10k.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/zhtext_10k.dart FILE: ../../../third_party/dart/client/idea/.idea/.name FILE: ../../../third_party/dart/client/idea/.idea/inspectionProfiles/Project_Default.xml FILE: ../../../third_party/dart/client/idea/.idea/vcs.xml @@ -7740,6 +7746,7 @@ ORIGIN: ../../../third_party/dart/benchmarks/ListCopy/dart/ListCopy.dart + ../.. TYPE: LicenseType.bsd FILE: ../../../third_party/dart/benchmarks/ListCopy/dart/ListCopy.dart FILE: ../../../third_party/dart/benchmarks/TypedDataDuplicate/dart/TypedDataDuplicate.dart +FILE: ../../../third_party/dart/benchmarks/Utf8Decode/dart/Utf8Decode.dart FILE: ../../../third_party/dart/runtime/bin/dartdev_utils.cc FILE: ../../../third_party/dart/runtime/bin/dartdev_utils.h FILE: ../../../third_party/dart/runtime/bin/exe_utils.cc @@ -7751,6 +7758,7 @@ FILE: ../../../third_party/dart/runtime/tools/wiki/xref_extractor/lib/xref_extra FILE: ../../../third_party/dart/runtime/vm/compiler/aot/dispatch_table_generator.cc FILE: ../../../third_party/dart/runtime/vm/compiler/aot/dispatch_table_generator.h FILE: ../../../third_party/dart/runtime/vm/compiler/assembler/assembler_base.h +FILE: ../../../third_party/dart/runtime/vm/compiler/backend/reachability_fence_test.cc FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/abi.cc FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/abi.h FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/call.cc @@ -7789,8 +7797,10 @@ FILE: ../../../third_party/dart/samples/ffi/async/sample_async_callback.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_native_port_call.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_patch.dart +FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/js_patch.dart +FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/vm/lib/ffi_struct_patch.dart ---------------------------------------------------------------------------------------------------- Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file for details. All rights reserved. diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index e7c70e51f9a06..571c3a18d8f9d 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -230,7 +230,7 @@ void _runMainZoned(Function startMainIsolateFunction, Function userMainFunction, List args) { startMainIsolateFunction((){ - runZoned(() { + runZonedGuarded(() { if (userMainFunction is _BinaryFunction) { // This seems to be undocumented but supported by the command line VM. // Let's do the same in case old entry-points are ported to Flutter. @@ -240,7 +240,7 @@ void _runMainZoned(Function startMainIsolateFunction, } else { userMainFunction(); } - }, onError: (Object error, StackTrace stackTrace) { + }, (Object error, StackTrace stackTrace) { _reportUnhandledException(error.toString(), stackTrace.toString()); }); }, null); diff --git a/testing/scenario_app/lib/src/animated_color_square.dart b/testing/scenario_app/lib/src/animated_color_square.dart index 8a5e51570926c..f1e9865f55309 100644 --- a/testing/scenario_app/lib/src/animated_color_square.dart +++ b/testing/scenario_app/lib/src/animated_color_square.dart @@ -99,7 +99,7 @@ class _NumberSwinger { } else if (_current <= _begin) { _up = true; } - _current = _up ? _current + 1 : _current - 1; + _current = (_up ? _current + 1 : _current - 1) as T; return _current; } } From 40931c8fe5926b51be8cdc42fd41894088e999b7 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Mon, 30 Mar 2020 16:42:36 -0700 Subject: [PATCH 186/386] [web] Fix HtmlImage state pollution due to image.clone optimization (#17370) * fix drawImage style when destination width==source * Add regression test * dartfmt * update golden lock * Addressed review comment * remove transform reset since _drawImage is called --- lib/web_ui/lib/src/engine/bitmap_canvas.dart | 9 +- .../engine/canvas_draw_picture_test.dart | 122 ++++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 lib/web_ui/test/golden_tests/engine/canvas_draw_picture_test.dart diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index f14fedb652a6a..5444086f8c24c 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -364,6 +364,10 @@ class BitmapCanvas extends EngineCanvas { final ui.BlendMode blendMode = paint.blendMode; imgElement.style.mixBlendMode = _stringForBlendMode(blendMode); if (_canvasPool.isClipped) { + // Reset width/height since they may have been previously set. + imgElement.style + ..removeProperty('width') + ..removeProperty('height'); final List clipElements = _clipContent( _canvasPool._clipStack, imgElement, p, _canvasPool.currentTransform); for (html.Element clipElement in clipElements) { @@ -375,7 +379,10 @@ class BitmapCanvas extends EngineCanvas { transformWithOffset(_canvasPool.currentTransform, p).storage); imgElement.style ..transformOrigin = '0 0 0' - ..transform = cssTransform; + ..transform = cssTransform + // Reset width/height since they may have been previously set. + ..removeProperty('width') + ..removeProperty('height'); rootElement.append(imgElement); _children.add(imgElement); } diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_picture_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_picture_test.dart new file mode 100644 index 0000000000000..de4c52cebb1d2 --- /dev/null +++ b/lib/web_ui/test/golden_tests/engine/canvas_draw_picture_test.dart @@ -0,0 +1,122 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.6 +import 'dart:html' as html; + +import 'package:ui/ui.dart'; +import 'package:ui/src/engine.dart'; +import 'package:test/test.dart'; + +import 'package:web_engine_tester/golden_tester.dart'; + +final Rect region = Rect.fromLTWH(0, 0, 500, 100); + +void main() async { + setUp(() async { + debugShowClipLayers = true; + SurfaceSceneBuilder.debugForgetFrameScene(); + for (html.Node scene in html.document.querySelectorAll('flt-scene')) { + scene.remove(); + } + + await webOnlyInitializePlatform(); + webOnlyFontCollection.debugRegisterTestFonts(); + await webOnlyFontCollection.ensureFontsLoaded(); + }); + + test('draw growing picture across frames', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + + _drawTestPicture(builder, 100, false); + builder.pop(); + + html.Element elm1 = builder.build().webOnlyRootElement; + html.document.body.append(elm1); + + // Now draw picture again but at larger size. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + // Now draw the picture at original target size, which will use a + // different code path that should normally not have width/height set + // on image element. + _drawTestPicture(builder2, 20, false); + builder2.pop(); + + elm1.remove(); + html.document.body.append(builder2.build().webOnlyRootElement); + + await matchGoldenFile('canvas_draw_picture_acrossframes.png', + region: region); + }); + + test('draw growing picture across frames clipped', () async { + final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); + builder.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + + _drawTestPicture(builder, 100, true); + builder.pop(); + + html.Element elm1 = builder.build().webOnlyRootElement; + html.document.body.append(elm1); + + // Now draw picture again but at larger size. + final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); + builder2.pushClipRect( + const Rect.fromLTRB(0, 0, 100, 100), + ); + _drawTestPicture(builder2, 20, true); + builder2.pop(); + + elm1.remove(); + html.document.body.append(builder2.build().webOnlyRootElement); + + await matchGoldenFile('canvas_draw_picture_acrossframes_clipped.png', + region: region); + }); +} + +HtmlImage sharedImage; + +void _drawTestPicture(SceneBuilder builder, double targetSize, bool clipped) { + sharedImage ??= _createRealTestImage(); + final EnginePictureRecorder recorder = PictureRecorder(); + final RecordingCanvas canvas = + recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); + canvas.debugEnforceArbitraryPaint(); + if (clipped) { + canvas.clipRRect( + RRect.fromLTRBR(0, 0, targetSize, targetSize, Radius.circular(4))); + } + canvas.drawImageRect(sharedImage, Rect.fromLTWH(0, 0, 20, 20), + Rect.fromLTWH(0, 0, targetSize, targetSize), Paint()); + final Picture picture = recorder.endRecording(); + builder.addPicture( + Offset.zero, + picture, + ); +} + +typedef PaintCallback = void Function(RecordingCanvas canvas); + +const String _base64Encoded20x20TestImage = + 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' + 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' + 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; + +HtmlImage _createRealTestImage() { + return HtmlImage( + html.ImageElement() + ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', + 20, + 20, + ); +} From 10887c2abe7b7fe5866e84d6b63dd546f02fc963 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Mon, 30 Mar 2020 16:45:51 -0700 Subject: [PATCH 187/386] Document flutter::SkiaConcurrentExecutor. (#17394) --- runtime/skia_concurrent_executor.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/runtime/skia_concurrent_executor.h b/runtime/skia_concurrent_executor.h index e1a506c033ab6..87dde23936325 100644 --- a/runtime/skia_concurrent_executor.h +++ b/runtime/skia_concurrent_executor.h @@ -11,13 +11,38 @@ namespace flutter { +//------------------------------------------------------------------------------ +/// @brief An interface used by Skia to schedule work on engine managed +/// threads (usually workers in a concurrent message loop). +/// +/// Skia may decide that certain workloads don't have thread +/// affinity and may be performed on a background thread. However, +/// Skia does not manage its own threads. So, it delegates the +/// scheduling of this work to the engine via this interface. The +/// engine has a dedicated pool of threads it uses for scheduling +/// background tasks that have no thread affinity. This thread +/// worker pool is held next to the process global Dart VM instance. +/// The Skia executor is wired up there as well. +/// class SkiaConcurrentExecutor : public SkExecutor { public: + //---------------------------------------------------------------------------- + /// The callback invoked by the executor to schedule the given task onto an + /// engine managed background thread. + /// using OnWorkCallback = std::function; + + //---------------------------------------------------------------------------- + /// @brief Create a new instance of the executor. + /// + /// @param[in] on_work The work callback. + /// SkiaConcurrentExecutor(const OnWorkCallback& on_work); + // |SkExecutor| ~SkiaConcurrentExecutor() override; + // |SkExecutor| void add(fml::closure work) override; private: From f1261a2abbc4c7183f73eb419cff1f9c15539967 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 08:46:01 -0400 Subject: [PATCH 188/386] Roll src/third_party/dart 1402e8e1a42d..11d6bc605512 (100 commits) (#17405) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 673a911e306c6..8b4815240919d 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'e21c4353006d665e7c4378413ad4d237f18437e0', + 'dart_revision': '11d6bc6055129da2a3b2f9db6bb44332b2744d1d', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 34b5fae936912..069f73f30df2e 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f410ed851c283cb74c817135c16276f4 +Signature: a92f7501d2812d244d28ad2c1a01bfe7 UNUSED LICENSES: From ae2ecf96c45438d8928f9c0ca72ec9d77f050747 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 08:51:02 -0400 Subject: [PATCH 189/386] Roll fuchsia/sdk/core/mac-amd64 from d4Bft... to lXWVX... (#17409) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 8b4815240919d..2ec6d92bf8de7 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'd4Bftt0zPSH0X7YiI1VyGEN_i7LM-HCEexU4Qn3OmxIC' + 'version': 'lXWVXH_LU6gBBCNQF3ww4ya0gqknw-NtKidBABWr1UMC' } ], 'condition': 'host_os == "mac"', From e9f96e59f567965a6a5013799eba75378b3d7f53 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 08:56:03 -0400 Subject: [PATCH 190/386] Roll src/third_party/skia e9bc857b39c2..732f84d3e279 (25 commits) (#17411) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/DEPS b/DEPS index 2ec6d92bf8de7..4470178647d73 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e9bc857b39c22878fd2f46284d83f22fe45540d9', + 'skia_revision': '732f84d3e279392b835d48a9b5f3d882c490c0da', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 66c7873e699a2..865a27846fdea 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 3f2ce43aca5125ec5f543abede6a3203 +Signature: 9f54562b8f9db43f542e45169423307c UNUSED LICENSES: @@ -486,7 +486,6 @@ FILE: ../../../third_party/skia/gm/hardstop_gradients.cpp FILE: ../../../third_party/skia/gm/imagemakewithfilter.cpp FILE: ../../../third_party/skia/gm/imagemasksubset.cpp FILE: ../../../third_party/skia/gm/lattice.cpp -FILE: ../../../third_party/skia/gm/lightingshader2.cpp FILE: ../../../third_party/skia/gm/overdrawcolorfilter.cpp FILE: ../../../third_party/skia/gm/overstroke.cpp FILE: ../../../third_party/skia/gm/pathmaskcache.cpp @@ -534,7 +533,6 @@ FILE: ../../../third_party/skia/modules/skshaper/src/SkShaper_primitive.cpp FILE: ../../../third_party/skia/samplecode/DecodeFile.h FILE: ../../../third_party/skia/samplecode/Sample.cpp FILE: ../../../third_party/skia/samplecode/SampleAndroidShadows.cpp -FILE: ../../../third_party/skia/samplecode/SampleLitAtlas.cpp FILE: ../../../third_party/skia/samplecode/SampleMegaStroke.cpp FILE: ../../../third_party/skia/samplecode/SamplePathOverstroke.cpp FILE: ../../../third_party/skia/samplecode/SampleSVGFile.cpp @@ -571,12 +569,6 @@ FILE: ../../../third_party/skia/src/core/SkLeanWindows.h FILE: ../../../third_party/skia/src/core/SkMSAN.h FILE: ../../../third_party/skia/src/core/SkMatrixPriv.h FILE: ../../../third_party/skia/src/core/SkModeColorFilter.h -FILE: ../../../third_party/skia/src/core/SkNormalFlatSource.cpp -FILE: ../../../third_party/skia/src/core/SkNormalFlatSource.h -FILE: ../../../third_party/skia/src/core/SkNormalMapSource.cpp -FILE: ../../../third_party/skia/src/core/SkNormalMapSource.h -FILE: ../../../third_party/skia/src/core/SkNormalSource.cpp -FILE: ../../../third_party/skia/src/core/SkNormalSource.h FILE: ../../../third_party/skia/src/core/SkOverdrawCanvas.cpp FILE: ../../../third_party/skia/src/core/SkPathMeasurePriv.h FILE: ../../../third_party/skia/src/core/SkRasterPipeline.cpp @@ -704,7 +696,6 @@ FILE: ../../../third_party/skia/src/ports/SkImageGeneratorCG.cpp FILE: ../../../third_party/skia/src/ports/SkImageGeneratorWIC.cpp FILE: ../../../third_party/skia/src/shaders/SkColorFilterShader.h FILE: ../../../third_party/skia/src/shaders/SkColorShader.cpp -FILE: ../../../third_party/skia/src/shaders/SkLights.cpp FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.cpp FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientBase.h FILE: ../../../third_party/skia/src/shaders/gradients/Sk4fGradientPriv.h @@ -2278,7 +2269,6 @@ FILE: ../../../third_party/skia/gm/imagesource2.cpp FILE: ../../../third_party/skia/gm/largeglyphblur.cpp FILE: ../../../third_party/skia/gm/lcdblendmodes.cpp FILE: ../../../third_party/skia/gm/lcdoverlap.cpp -FILE: ../../../third_party/skia/gm/lightingshader.cpp FILE: ../../../third_party/skia/gm/localmatriximagefilter.cpp FILE: ../../../third_party/skia/gm/localmatriximageshader.cpp FILE: ../../../third_party/skia/gm/mipmap.cpp @@ -2338,7 +2328,6 @@ FILE: ../../../third_party/skia/samplecode/SampleAnimatedText.cpp FILE: ../../../third_party/skia/samplecode/SampleAtlas.cpp FILE: ../../../third_party/skia/samplecode/SampleClipDrawMatch.cpp FILE: ../../../third_party/skia/samplecode/SampleFilterQuality.cpp -FILE: ../../../third_party/skia/samplecode/SampleLighting.cpp FILE: ../../../third_party/skia/samplecode/SampleShip.cpp FILE: ../../../third_party/skia/samplecode/SampleXfer.cpp FILE: ../../../third_party/skia/src/android/SkBitmapRegionCodec.cpp @@ -2582,9 +2571,6 @@ FILE: ../../../third_party/skia/src/ports/SkOSLibrary_posix.cpp FILE: ../../../third_party/skia/src/ports/SkOSLibrary_win.cpp FILE: ../../../third_party/skia/src/shaders/SkImageShader.cpp FILE: ../../../third_party/skia/src/shaders/SkImageShader.h -FILE: ../../../third_party/skia/src/shaders/SkLightingShader.cpp -FILE: ../../../third_party/skia/src/shaders/SkLightingShader.h -FILE: ../../../third_party/skia/src/shaders/SkLights.h FILE: ../../../third_party/skia/src/svg/SkSVGCanvas.cpp FILE: ../../../third_party/skia/src/svg/SkSVGDevice.cpp FILE: ../../../third_party/skia/src/svg/SkSVGDevice.h From 6b09714a2c886a5bad9e5aafb425fd6ae4049fac Mon Sep 17 00:00:00 2001 From: Ferhat Date: Tue, 31 Mar 2020 09:43:14 -0700 Subject: [PATCH 191/386] Add regression test for treeshaking debugFillProperties (#17325) * Add test to make sure we don't regress treeshaking of debugFillProperties * update cirrus --- .cirrus.yml | 1 + .../lib/treeshaking_main.dart | 45 ++++++++++++++ .../test_driver/treeshaking_e2e.dart | 61 +++++++++++++++++++ .../test_driver/treeshaking_e2e_test.dart | 7 +++ 4 files changed, 114 insertions(+) create mode 100644 e2etests/web/regular_integration_tests/lib/treeshaking_main.dart create mode 100644 e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e.dart create mode 100644 e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index 7b954671a7aeb..88f3c3e71319e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -156,6 +156,7 @@ task: - $FRAMEWORK_PATH/flutter/bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web - $FRAMEWORK_PATH/flutter/bin/flutter pub get --local-engine=host_debug_unopt - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/text_editing_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt + - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/treeshaking_e2e.dart -d web-server --profile --browser-name=chrome --local-engine=host_debug_unopt - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/image_loading_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt - name: build_and_test_web_linux_firefox diff --git a/e2etests/web/regular_integration_tests/lib/treeshaking_main.dart b/e2etests/web/regular_integration_tests/lib/treeshaking_main.dart new file mode 100644 index 0000000000000..0df900d0597b4 --- /dev/null +++ b/e2etests/web/regular_integration_tests/lib/treeshaking_main.dart @@ -0,0 +1,45 @@ + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +void main() => runApp(MyApp()); + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + key: const Key('mainapp'), + title: 'Integration Test App', + home: MyHomePage(title: 'Integration Test App'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final TextEditingController _controller = + TextEditingController(text: 'TreeshakingThings1'); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: const Center( + child: Text('TreeshakingThings'), + ), + ); + } +} diff --git a/e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e.dart b/e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e.dart new file mode 100644 index 0000000000000..30758c555f5b0 --- /dev/null +++ b/e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e.dart @@ -0,0 +1,61 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +import 'package:flutter_test/flutter_test.dart'; +import 'package:regular_integration_tests/treeshaking_main.dart' as app; +import 'package:flutter/material.dart'; + +import 'package:e2e/e2e.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + testWidgets('debug+Fill+Properties for widgets is tree shaken', + (WidgetTester tester) async { + // About 11 instances are used by DiagnosticsNode and diagnostics + // for flutter framework itself. Widgets have > 100. So we check for 20 to + // so this test fails when tree-shaking is broken. + await testOccurenceCountBelow(tester, '${debugPrefix}FillProperties', 20); + }); +} + +// Used to prevent compiler optimization that will generate const string. +// Preventing counting test strings. +String get debugPrefix => ['d','e','b','u','g'].join(''); + +Future testOccurenceCountBelow(WidgetTester tester, String methodName, int count) async { + app.main(); + await tester.pumpAndSettle(); + + // Make sure app loaded. + final Finder finder = find.byKey(const Key('mainapp')); + expect(finder, findsOneWidget); + + await _loadBundleAndCheck(methodName, count); +} + +String fileContents; + +Future _loadBundleAndCheck(String methodName, int count) async { + fileContents ??= await html.HttpRequest.getString('main.dart.js'); + expect(fileContents, contains('RenderObjectToWidgetElement')); + expect(occurrenceCount(fileContents, methodName), lessThan(count)); +} + +int occurrenceCount(String contents, String word) { + int count = 0; + final int wordLength = word.length; + int pos = contents.indexOf(word); + final int contentLength = contents.length; + while (pos != -1) { + ++count; + pos += wordLength; + if (pos >= contentLength || count > 100) { + break; + } + pos = contents.indexOf(word, pos); + } + return count; +} diff --git a/e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e_test.dart b/e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e_test.dart new file mode 100644 index 0000000000000..a29203f7dcdd9 --- /dev/null +++ b/e2etests/web/regular_integration_tests/test_driver/treeshaking_e2e_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); From 7932c87be7e199e10f790a868790696a5e47125e Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 31 Mar 2020 10:21:02 -0700 Subject: [PATCH 192/386] Correct --dry-run hint (#17413) --- ci/check_gn_format.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/check_gn_format.py b/ci/check_gn_format.py index d7611a4f91415..2257a58984dc6 100755 --- a/ci/check_gn_format.py +++ b/ci/check_gn_format.py @@ -41,8 +41,8 @@ def main(): for gn_file in GetGNFiles(args.root_directory): if subprocess.call(gn_command + [ gn_file ]) != 0: print "ERROR: '%s' is incorrectly formatted." % os.path.relpath(gn_file, args.root_directory) - print "Format the same with 'gn format' using the 'gn' binary in //buildtools." - print "Or, run ./ci/check_gn_format.py with '--dry-run false'" + print "Format the same with 'gn format' using the 'gn' binary in third_party/gn/gn." + print "Or, run ./ci/check_gn_format.py without '--dry-run'" return 1 return 0 From 6ae081317c38fb78c0684a5c5013283cd3eef300 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 13:26:03 -0400 Subject: [PATCH 193/386] Roll fuchsia/sdk/core/mac-amd64 from lXWVX... to lPmiR... (#17417) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 4470178647d73..66990ed0c53f2 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'lXWVXH_LU6gBBCNQF3ww4ya0gqknw-NtKidBABWr1UMC' + 'version': 'lPmiRXjvD_DKq35O_7qZIgkNQLduwHGp6UbVp6HV3bAC' } ], 'condition': 'host_os == "mac"', From 816619709910aa55d1e06506a0ff71e45346ca62 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 13:31:03 -0400 Subject: [PATCH 194/386] Roll src/third_party/skia 732f84d3e279..65d3ad953957 (3 commits) (#17418) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 66990ed0c53f2..430ecba1c20fd 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '732f84d3e279392b835d48a9b5f3d882c490c0da', + 'skia_revision': '65d3ad95395791de882d4c8e4804803903534dce', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 865a27846fdea..d20fdea37c559 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9f54562b8f9db43f542e45169423307c +Signature: 4a73f5eb2f083e66b227356170f0c90f UNUSED LICENSES: From fc5b44c901ceb37d3f98715b2b9d857ca64e4db6 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 14:52:09 -0400 Subject: [PATCH 195/386] Roll src/third_party/dart 11d6bc605512..e8c4aed700f5 (20 commits) (#17421) https://dart.googlesource.com/sdk.git/+log/11d6bc605512..e8c4aed700f5 git log 11d6bc605512..e8c4aed700f5 --date=short --first-parent --format='%ad %ae %s' 2020-03-31 scheglov@google.com Remove implementedForMixing from inheritance manager. 2020-03-31 scheglov@google.com Fix for crash in WrapInText when the parameter is not InterfaceType. 2020-03-31 rnystrom@google.com Migrate language_2/compile_time_constant to NNBD. 2020-03-31 alexmarkov@google.com [vm/nnbd] Do not treat late final fields as immutable 2020-03-31 lrn@google.com Add specialized classes for ZoneFunction to handle generics. 2020-03-31 sstrickl@google.com [vm] Check the global object pool (if any) when visiting functions. 2020-03-31 jensj@google.com [CFE] Add "annotate lines" option to parser suite 2020-03-31 sortie@google.com [dart:_http] Fix HeaderValue.parse if valueSeparator is parameterSeparator. 2020-03-31 sigurdm@google.com Bump pub revision 2020-03-31 kustermann@google.com [infra] Update tools/test.py and test matrix for running tests with arm64-qemu 2020-03-31 athom@google.com [co19] Roll co19 to 9934d8540d097366287267519413e888a7f6a482 2020-03-31 dmitryas@google.com [cfe] Apply the special treatment of FutureOr in GLB for NNBD 2020-03-31 karlklose@google.com [infra] Remove unused bot script 2020-03-31 jensj@google.com [CFE] Perform transformPostInference when doing expression compilation 2020-03-31 karlklose@google.com [infra] Remove creation of unused file 2020-03-31 asiva@google.com [Tests] Skip debugger_location_test (see https://github.com/dart-lang/sdk/issues/41219) 2020-03-31 rnystrom@google.com Migrate language_2/canonicalize to NNBD. 2020-03-30 srujzs@google.com [dart:html] Add bool type to supportsTypedData 2020-03-30 scheglov@google.com Deprecate isProxy / isOrInheritsProxy in ClassElement. 2020-03-30 scheglov@google.com Set dynamic resolution for super MethodInvocation in invalid context. Created with: gclient setdep -r src/third_party/dart@e8c4aed700f5 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 430ecba1c20fd..a4bc24b9c9f0a 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '11d6bc6055129da2a3b2f9db6bb44332b2744d1d', + 'dart_revision': 'e8c4aed700f5effdab0ac76497974e2289b589c9', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -76,7 +76,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '8777719d1a8261b42a81b3b4025b7d695fe0c683', + 'dart_pub_rev': '5802dd3407cae29372060934249432077aff1ec6', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 069f73f30df2e..d27e96e72496a 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: a92f7501d2812d244d28ad2c1a01bfe7 +Signature: 7add498ea3984173a26da831c3ee3445 UNUSED LICENSES: From 807ca8543cf1691578cfd01a2efa72d19a84d4c9 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 31 Mar 2020 14:05:28 -0700 Subject: [PATCH 196/386] More rename from GPU thread to raster thread (#17408) This PR touches variable names, class names, and file names so it's significantly more risky than its predecessor https://github.com/flutter/engine/pull/17329 Due to file name changes, this PR is expected to change the license files. We haven't rename `shell/gpu` to `shell/raster` yet. It should be optional but I think it's better to have `raster_surface_software.cc` than `gpu_surface_software.cc`. --- ci/licenses_golden/licenses_flutter | 6 +- common/task_runners.cc | 10 +-- common/task_runners.h | 6 +- flow/compositor_context.cc | 13 +-- flow/compositor_context.h | 8 +- flow/embedded_views.h | 4 +- fml/BUILD.gn | 6 +- ...read_merger.cc => raster_thread_merger.cc} | 26 +++--- ...thread_merger.h => raster_thread_merger.h} | 25 +++--- ...s.cc => raster_thread_merger_unittests.cc} | 62 ++++++------- lib/ui/compositing/scene_host.cc | 13 +-- lib/ui/compositing/scene_host.h | 2 +- lib/ui/painting/image_encoding.cc | 16 ++-- lib/ui/painting/picture.cc | 4 +- runtime/dart_isolate.cc | 8 +- shell/common/animator.cc | 2 +- shell/common/platform_view.cc | 2 +- shell/common/rasterizer.cc | 23 ++--- shell/common/rasterizer.h | 4 +- shell/common/shell.cc | 90 +++++++++---------- shell/common/shell_benchmarks.cc | 2 +- shell/common/shell_test.cc | 2 +- shell/common/shell_unittests.cc | 12 +-- shell/common/thread_host.cc | 4 +- shell/common/thread_host.h | 2 +- .../platform/android/android_shell_holder.cc | 6 +- .../platform/android/platform_view_android.cc | 6 +- .../ios/framework/Source/FlutterEngine.mm | 6 +- .../framework/Source/FlutterEngine_Internal.h | 2 +- .../framework/Source/FlutterPlatformViews.mm | 8 +- .../Source/FlutterPlatformViews_Internal.h | 2 +- .../framework/Source/FlutterViewController.mm | 4 +- shell/platform/darwin/ios/ios_surface.h | 3 +- shell/platform/darwin/ios/ios_surface.mm | 4 +- .../platform/darwin/ios/platform_view_ios.mm | 2 +- shell/platform/embedder/embedder_engine.cc | 5 +- .../platform/embedder/embedder_thread_host.cc | 10 +-- .../embedder/tests/embedder_unittests.cc | 4 +- .../fuchsia/flutter/compositor_context.cc | 2 +- .../fuchsia/flutter/compositor_context.h | 2 +- shell/platform/fuchsia/flutter/engine.cc | 8 +- shell/testing/tester_main.cc | 10 +-- 42 files changed, 223 insertions(+), 213 deletions(-) rename fml/{gpu_thread_merger.cc => raster_thread_merger.cc} (68%) rename fml/{gpu_thread_merger.h => raster_thread_merger.h} (69%) rename fml/{gpu_thread_merger_unittests.cc => raster_thread_merger_unittests.cc} (73%) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f9cfcab303213..903a33b252e51 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -134,9 +134,6 @@ FILE: ../../../flutter/fml/eintr_wrapper.h FILE: ../../../flutter/fml/file.cc FILE: ../../../flutter/fml/file.h FILE: ../../../flutter/fml/file_unittest.cc -FILE: ../../../flutter/fml/gpu_thread_merger.cc -FILE: ../../../flutter/fml/gpu_thread_merger.h -FILE: ../../../flutter/fml/gpu_thread_merger_unittests.cc FILE: ../../../flutter/fml/hash_combine.h FILE: ../../../flutter/fml/hash_combine_unittests.cc FILE: ../../../flutter/fml/icu_util.cc @@ -229,6 +226,9 @@ FILE: ../../../flutter/fml/platform/win/paths_win.cc FILE: ../../../flutter/fml/platform/win/posix_wrappers_win.cc FILE: ../../../flutter/fml/platform/win/wstring_conversion.h FILE: ../../../flutter/fml/posix_wrappers.h +FILE: ../../../flutter/fml/raster_thread_merger.cc +FILE: ../../../flutter/fml/raster_thread_merger.h +FILE: ../../../flutter/fml/raster_thread_merger_unittests.cc FILE: ../../../flutter/fml/size.h FILE: ../../../flutter/fml/status.h FILE: ../../../flutter/fml/synchronization/atomic_object.h diff --git a/common/task_runners.cc b/common/task_runners.cc index baa0d2b132c91..867fce72e3a11 100644 --- a/common/task_runners.cc +++ b/common/task_runners.cc @@ -10,12 +10,12 @@ namespace flutter { TaskRunners::TaskRunners(std::string label, fml::RefPtr platform, - fml::RefPtr gpu, + fml::RefPtr raster, fml::RefPtr ui, fml::RefPtr io) : label_(std::move(label)), platform_(std::move(platform)), - gpu_(std::move(gpu)), + raster_(std::move(raster)), ui_(std::move(ui)), io_(std::move(io)) {} @@ -39,12 +39,12 @@ fml::RefPtr TaskRunners::GetIOTaskRunner() const { return io_; } -fml::RefPtr TaskRunners::GetGPUTaskRunner() const { - return gpu_; +fml::RefPtr TaskRunners::GetRasterTaskRunner() const { + return raster_; } bool TaskRunners::IsValid() const { - return platform_ && gpu_ && ui_ && io_; + return platform_ && raster_ && ui_ && io_; } } // namespace flutter diff --git a/common/task_runners.h b/common/task_runners.h index 6e08abcad1d1d..291372b785325 100644 --- a/common/task_runners.h +++ b/common/task_runners.h @@ -16,7 +16,7 @@ class TaskRunners { public: TaskRunners(std::string label, fml::RefPtr platform, - fml::RefPtr gpu, + fml::RefPtr raster, fml::RefPtr ui, fml::RefPtr io); @@ -32,14 +32,14 @@ class TaskRunners { fml::RefPtr GetIOTaskRunner() const; - fml::RefPtr GetGPUTaskRunner() const; + fml::RefPtr GetRasterTaskRunner() const; bool IsValid() const; private: const std::string label_; fml::RefPtr platform_; - fml::RefPtr gpu_; + fml::RefPtr raster_; fml::RefPtr ui_; fml::RefPtr io_; }; diff --git a/flow/compositor_context.cc b/flow/compositor_context.cc index d816ca53ef877..e94ffd53f53d7 100644 --- a/flow/compositor_context.cc +++ b/flow/compositor_context.cc @@ -37,10 +37,10 @@ std::unique_ptr CompositorContext::AcquireFrame( const SkMatrix& root_surface_transformation, bool instrumentation_enabled, bool surface_supports_readback, - fml::RefPtr gpu_thread_merger) { + fml::RefPtr raster_thread_merger) { return std::make_unique( *this, gr_context, canvas, view_embedder, root_surface_transformation, - instrumentation_enabled, surface_supports_readback, gpu_thread_merger); + instrumentation_enabled, surface_supports_readback, raster_thread_merger); } CompositorContext::ScopedFrame::ScopedFrame( @@ -51,7 +51,7 @@ CompositorContext::ScopedFrame::ScopedFrame( const SkMatrix& root_surface_transformation, bool instrumentation_enabled, bool surface_supports_readback, - fml::RefPtr gpu_thread_merger) + fml::RefPtr raster_thread_merger) : context_(context), gr_context_(gr_context), canvas_(canvas), @@ -59,7 +59,7 @@ CompositorContext::ScopedFrame::ScopedFrame( root_surface_transformation_(root_surface_transformation), instrumentation_enabled_(instrumentation_enabled), surface_supports_readback_(surface_supports_readback), - gpu_thread_merger_(gpu_thread_merger) { + raster_thread_merger_(raster_thread_merger) { context_.BeginFrame(*this, instrumentation_enabled_); } @@ -74,8 +74,9 @@ RasterStatus CompositorContext::ScopedFrame::Raster( bool root_needs_readback = layer_tree.Preroll(*this, ignore_raster_cache); bool needs_save_layer = root_needs_readback && !surface_supports_readback(); PostPrerollResult post_preroll_result = PostPrerollResult::kSuccess; - if (view_embedder_ && gpu_thread_merger_) { - post_preroll_result = view_embedder_->PostPrerollAction(gpu_thread_merger_); + if (view_embedder_ && raster_thread_merger_) { + post_preroll_result = + view_embedder_->PostPrerollAction(raster_thread_merger_); } if (post_preroll_result == PostPrerollResult::kResubmitFrame) { diff --git a/flow/compositor_context.h b/flow/compositor_context.h index 6698d3f41a2f1..b932594be5d71 100644 --- a/flow/compositor_context.h +++ b/flow/compositor_context.h @@ -12,8 +12,8 @@ #include "flutter/flow/instrumentation.h" #include "flutter/flow/raster_cache.h" #include "flutter/flow/texture.h" -#include "flutter/fml/gpu_thread_merger.h" #include "flutter/fml/macros.h" +#include "flutter/fml/raster_thread_merger.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPictureRecorder.h" @@ -46,7 +46,7 @@ class CompositorContext { const SkMatrix& root_surface_transformation, bool instrumentation_enabled, bool surface_supports_readback, - fml::RefPtr gpu_thread_merger); + fml::RefPtr raster_thread_merger); virtual ~ScopedFrame(); @@ -75,7 +75,7 @@ class CompositorContext { const SkMatrix& root_surface_transformation_; const bool instrumentation_enabled_; const bool surface_supports_readback_; - fml::RefPtr gpu_thread_merger_; + fml::RefPtr raster_thread_merger_; FML_DISALLOW_COPY_AND_ASSIGN(ScopedFrame); }; @@ -91,7 +91,7 @@ class CompositorContext { const SkMatrix& root_surface_transformation, bool instrumentation_enabled, bool surface_supports_readback, - fml::RefPtr gpu_thread_merger); + fml::RefPtr raster_thread_merger); void OnGrContextCreated(); diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 7a491a8a152ef..b80f7b4ba5d54 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -7,8 +7,8 @@ #include -#include "flutter/fml/gpu_thread_merger.h" #include "flutter/fml/memory/ref_counted.h" +#include "flutter/fml/raster_thread_merger.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkPoint.h" @@ -239,7 +239,7 @@ class ExternalViewEmbedder { // after it does any requisite tasks needed to bring itself to a valid state. // Returns kSuccess if the view embedder is already in a valid state. virtual PostPrerollResult PostPrerollAction( - fml::RefPtr gpu_thread_merger) { + fml::RefPtr raster_thread_merger) { return PostPrerollResult::kSuccess; } diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 12d30ee83f8d8..9b5626e787b1c 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -27,8 +27,6 @@ source_set("fml") { "eintr_wrapper.h", "file.cc", "file.h", - "gpu_thread_merger.cc", - "gpu_thread_merger.h", "hash_combine.h", "icu_util.cc", "icu_util.h", @@ -61,6 +59,8 @@ source_set("fml") { "paths.cc", "paths.h", "posix_wrappers.h", + "raster_thread_merger.cc", + "raster_thread_merger.h", "size.h", "synchronization/atomic_object.h", "synchronization/count_down_latch.cc", @@ -232,7 +232,6 @@ executable("fml_unittests") { "base32_unittest.cc", "command_line_unittest.cc", "file_unittest.cc", - "gpu_thread_merger_unittests.cc", "hash_combine_unittests.cc", "memory/ref_counted_unittest.cc", "memory/weak_ptr_unittest.cc", @@ -241,6 +240,7 @@ executable("fml_unittests") { "message_loop_unittests.cc", "message_unittests.cc", "paths_unittests.cc", + "raster_thread_merger_unittests.cc", "synchronization/count_down_latch_unittests.cc", "synchronization/semaphore_unittest.cc", "synchronization/sync_switch_unittest.cc", diff --git a/fml/gpu_thread_merger.cc b/fml/raster_thread_merger.cc similarity index 68% rename from fml/gpu_thread_merger.cc rename to fml/raster_thread_merger.cc index 9d0c2415b6a09..e81eebb84d3db 100644 --- a/fml/gpu_thread_merger.cc +++ b/fml/raster_thread_merger.cc @@ -4,15 +4,15 @@ #define FML_USED_ON_EMBEDDER -#include "flutter/fml/gpu_thread_merger.h" +#include "flutter/fml/raster_thread_merger.h" #include "flutter/fml/message_loop_impl.h" namespace fml { -const int GpuThreadMerger::kLeaseNotSet = -1; +const int RasterThreadMerger::kLeaseNotSet = -1; -GpuThreadMerger::GpuThreadMerger(fml::TaskQueueId platform_queue_id, - fml::TaskQueueId gpu_queue_id) +RasterThreadMerger::RasterThreadMerger(fml::TaskQueueId platform_queue_id, + fml::TaskQueueId gpu_queue_id) : platform_queue_id_(platform_queue_id), gpu_queue_id_(gpu_queue_id), task_queues_(fml::MessageLoopTaskQueues::GetInstance()), @@ -20,7 +20,7 @@ GpuThreadMerger::GpuThreadMerger(fml::TaskQueueId platform_queue_id, is_merged_ = task_queues_->Owns(platform_queue_id_, gpu_queue_id_); } -void GpuThreadMerger::MergeWithLease(size_t lease_term) { +void RasterThreadMerger::MergeWithLease(size_t lease_term) { FML_DCHECK(lease_term > 0) << "lease_term should be positive."; if (!is_merged_) { is_merged_ = task_queues_->Merge(platform_queue_id_, gpu_queue_id_); @@ -28,7 +28,7 @@ void GpuThreadMerger::MergeWithLease(size_t lease_term) { } } -bool GpuThreadMerger::IsOnRasterizingThread() { +bool RasterThreadMerger::IsOnRasterizingThread() { const auto current_queue_id = MessageLoop::GetCurrentTaskQueueId(); if (is_merged_) { return current_queue_id == platform_queue_id_; @@ -37,25 +37,25 @@ bool GpuThreadMerger::IsOnRasterizingThread() { } } -void GpuThreadMerger::ExtendLeaseTo(size_t lease_term) { +void RasterThreadMerger::ExtendLeaseTo(size_t lease_term) { FML_DCHECK(lease_term > 0) << "lease_term should be positive."; if (lease_term_ != kLeaseNotSet && (int)lease_term > lease_term_) { lease_term_ = lease_term; } } -bool GpuThreadMerger::IsMerged() const { +bool RasterThreadMerger::IsMerged() const { return is_merged_; } -GpuThreadStatus GpuThreadMerger::DecrementLease() { +RasterThreadStatus RasterThreadMerger::DecrementLease() { if (!is_merged_) { - return GpuThreadStatus::kRemainsUnmerged; + return RasterThreadStatus::kRemainsUnmerged; } // we haven't been set to merge. if (lease_term_ == kLeaseNotSet) { - return GpuThreadStatus::kRemainsUnmerged; + return RasterThreadStatus::kRemainsUnmerged; } FML_DCHECK(lease_term_ > 0) @@ -65,10 +65,10 @@ GpuThreadStatus GpuThreadMerger::DecrementLease() { bool success = task_queues_->Unmerge(platform_queue_id_); FML_CHECK(success) << "Unable to un-merge the GPU and platform threads."; is_merged_ = false; - return GpuThreadStatus::kUnmergedNow; + return RasterThreadStatus::kUnmergedNow; } - return GpuThreadStatus::kRemainsMerged; + return RasterThreadStatus::kRemainsMerged; } } // namespace fml diff --git a/fml/gpu_thread_merger.h b/fml/raster_thread_merger.h similarity index 69% rename from fml/gpu_thread_merger.h rename to fml/raster_thread_merger.h index 8a28a43f71dc2..847f7e3280aca 100644 --- a/fml/gpu_thread_merger.h +++ b/fml/raster_thread_merger.h @@ -13,9 +13,14 @@ namespace fml { class MessageLoopImpl; -enum class GpuThreadStatus { kRemainsMerged, kRemainsUnmerged, kUnmergedNow }; +enum class RasterThreadStatus { + kRemainsMerged, + kRemainsUnmerged, + kUnmergedNow +}; -class GpuThreadMerger : public fml::RefCountedThreadSafe { +class RasterThreadMerger + : public fml::RefCountedThreadSafe { public: // Merges the raster thread into platform thread for the duration of // the lease term. Lease is managed by the caller by either calling @@ -27,14 +32,14 @@ class GpuThreadMerger : public fml::RefCountedThreadSafe { void ExtendLeaseTo(size_t lease_term); - // Returns |GpuThreadStatus::kUnmergedNow| if this call resulted in splitting - // the GPU and platform threads. Reduces the lease term by 1. - GpuThreadStatus DecrementLease(); + // Returns |RasterThreadStatus::kUnmergedNow| if this call resulted in + // splitting the GPU and platform threads. Reduces the lease term by 1. + RasterThreadStatus DecrementLease(); bool IsMerged() const; - GpuThreadMerger(fml::TaskQueueId platform_queue_id, - fml::TaskQueueId gpu_queue_id); + RasterThreadMerger(fml::TaskQueueId platform_queue_id, + fml::TaskQueueId gpu_queue_id); // Returns true if the current thread owns rasterizing. // When the threads are merged, platform thread owns rasterizing. @@ -49,9 +54,9 @@ class GpuThreadMerger : public fml::RefCountedThreadSafe { std::atomic_int lease_term_; bool is_merged_; - FML_FRIEND_REF_COUNTED_THREAD_SAFE(GpuThreadMerger); - FML_FRIEND_MAKE_REF_COUNTED(GpuThreadMerger); - FML_DISALLOW_COPY_AND_ASSIGN(GpuThreadMerger); + FML_FRIEND_REF_COUNTED_THREAD_SAFE(RasterThreadMerger); + FML_FRIEND_MAKE_REF_COUNTED(RasterThreadMerger); + FML_DISALLOW_COPY_AND_ASSIGN(RasterThreadMerger); }; } // namespace fml diff --git a/fml/gpu_thread_merger_unittests.cc b/fml/raster_thread_merger_unittests.cc similarity index 73% rename from fml/gpu_thread_merger_unittests.cc rename to fml/raster_thread_merger_unittests.cc index 66f21c0fc494d..40ae9ba5351e2 100644 --- a/fml/gpu_thread_merger_unittests.cc +++ b/fml/raster_thread_merger_unittests.cc @@ -7,14 +7,14 @@ #include #include -#include "flutter/fml/gpu_thread_merger.h" #include "flutter/fml/message_loop.h" +#include "flutter/fml/raster_thread_merger.h" #include "flutter/fml/synchronization/count_down_latch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/task_runner.h" #include "gtest/gtest.h" -TEST(GpuThreadMerger, RemainMergedTillLeaseExpires) { +TEST(RasterThreadMerger, RemainMergedTillLeaseExpires) { fml::MessageLoop* loop1 = nullptr; fml::AutoResetWaitableEvent latch1; fml::AutoResetWaitableEvent term1; @@ -40,20 +40,20 @@ TEST(GpuThreadMerger, RemainMergedTillLeaseExpires) { fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto gpu_thread_merger_ = - fml::MakeRefCounted(qid1, qid2); + const auto raster_thread_merger_ = + fml::MakeRefCounted(qid1, qid2); const int kNumFramesMerged = 5; - ASSERT_FALSE(gpu_thread_merger_->IsMerged()); + ASSERT_FALSE(raster_thread_merger_->IsMerged()); - gpu_thread_merger_->MergeWithLease(kNumFramesMerged); + raster_thread_merger_->MergeWithLease(kNumFramesMerged); for (int i = 0; i < kNumFramesMerged; i++) { - ASSERT_TRUE(gpu_thread_merger_->IsMerged()); - gpu_thread_merger_->DecrementLease(); + ASSERT_TRUE(raster_thread_merger_->IsMerged()); + raster_thread_merger_->DecrementLease(); } - ASSERT_FALSE(gpu_thread_merger_->IsMerged()); + ASSERT_FALSE(raster_thread_merger_->IsMerged()); term1.Signal(); term2.Signal(); @@ -61,7 +61,7 @@ TEST(GpuThreadMerger, RemainMergedTillLeaseExpires) { thread2.join(); } -TEST(GpuThreadMerger, IsNotOnRasterizingThread) { +TEST(RasterThreadMerger, IsNotOnRasterizingThread) { fml::MessageLoop* loop1 = nullptr; fml::AutoResetWaitableEvent latch1; std::thread thread1([&loop1, &latch1]() { @@ -85,29 +85,29 @@ TEST(GpuThreadMerger, IsNotOnRasterizingThread) { fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto gpu_thread_merger_ = - fml::MakeRefCounted(qid1, qid2); + const auto raster_thread_merger_ = + fml::MakeRefCounted(qid1, qid2); fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2); loop1->GetTaskRunner()->PostTask([&]() { - ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread()); + ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); pre_merge.CountDown(); }); loop2->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2); pre_merge.CountDown(); }); pre_merge.Wait(); - gpu_thread_merger_->MergeWithLease(1); + raster_thread_merger_->MergeWithLease(1); loop1->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); post_merge.CountDown(); }); @@ -115,23 +115,23 @@ TEST(GpuThreadMerger, IsNotOnRasterizingThread) { loop2->GetTaskRunner()->PostTask([&]() { // this will be false since this is going to be run // on loop1 really. - ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); post_merge.CountDown(); }); post_merge.Wait(); - gpu_thread_merger_->DecrementLease(); + raster_thread_merger_->DecrementLease(); loop1->GetTaskRunner()->PostTask([&]() { - ASSERT_FALSE(gpu_thread_merger_->IsOnRasterizingThread()); + ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1); post_unmerge.CountDown(); }); loop2->GetTaskRunner()->PostTask([&]() { - ASSERT_TRUE(gpu_thread_merger_->IsOnRasterizingThread()); + ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread()); ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2); post_unmerge.CountDown(); }); @@ -146,7 +146,7 @@ TEST(GpuThreadMerger, IsNotOnRasterizingThread) { thread2.join(); } -TEST(GpuThreadMerger, LeaseExtension) { +TEST(RasterThreadMerger, LeaseExtension) { fml::MessageLoop* loop1 = nullptr; fml::AutoResetWaitableEvent latch1; fml::AutoResetWaitableEvent term1; @@ -172,30 +172,30 @@ TEST(GpuThreadMerger, LeaseExtension) { fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId(); fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId(); - const auto gpu_thread_merger_ = - fml::MakeRefCounted(qid1, qid2); + const auto raster_thread_merger_ = + fml::MakeRefCounted(qid1, qid2); const int kNumFramesMerged = 5; - ASSERT_FALSE(gpu_thread_merger_->IsMerged()); + ASSERT_FALSE(raster_thread_merger_->IsMerged()); - gpu_thread_merger_->MergeWithLease(kNumFramesMerged); + raster_thread_merger_->MergeWithLease(kNumFramesMerged); // let there be one more turn till the leases expire. for (int i = 0; i < kNumFramesMerged - 1; i++) { - ASSERT_TRUE(gpu_thread_merger_->IsMerged()); - gpu_thread_merger_->DecrementLease(); + ASSERT_TRUE(raster_thread_merger_->IsMerged()); + raster_thread_merger_->DecrementLease(); } // extend the lease once. - gpu_thread_merger_->ExtendLeaseTo(kNumFramesMerged); + raster_thread_merger_->ExtendLeaseTo(kNumFramesMerged); // we will NOT last for 1 extra turn, we just set it. for (int i = 0; i < kNumFramesMerged; i++) { - ASSERT_TRUE(gpu_thread_merger_->IsMerged()); - gpu_thread_merger_->DecrementLease(); + ASSERT_TRUE(raster_thread_merger_->IsMerged()); + raster_thread_merger_->DecrementLease(); } - ASSERT_FALSE(gpu_thread_merger_->IsMerged()); + ASSERT_FALSE(raster_thread_merger_->IsMerged()); term1.Signal(); term2.Signal(); diff --git a/lib/ui/compositing/scene_host.cc b/lib/ui/compositing/scene_host.cc index 763b2bcb27e40..83d0f561c1e2c 100644 --- a/lib/ui/compositing/scene_host.cc +++ b/lib/ui/compositing/scene_host.cc @@ -162,8 +162,8 @@ SceneHost::SceneHost(fml::RefPtr viewHolderToken, Dart_Handle viewConnectedCallback, Dart_Handle viewDisconnectedCallback, Dart_Handle viewStateChangedCallback) - : gpu_task_runner_( - UIDartState::Current()->GetTaskRunners().GetGPUTaskRunner()), + : raster_task_runner_( + UIDartState::Current()->GetTaskRunners().GetRasterTaskRunner()), koid_(GetKoid(viewHolderToken->handle())) { auto dart_state = UIDartState::Current(); isolate_service_id_ = Dart_IsolateServiceId(Dart_CurrentIsolate()); @@ -190,7 +190,7 @@ SceneHost::SceneHost(fml::RefPtr viewHolderToken, // Pass the raw handle to the GPU thead; destroying a |zircon::dart::Handle| // on that thread can cause a race condition. - gpu_task_runner_->PostTask( + raster_task_runner_->PostTask( [id = koid_, ui_task_runner = UIDartState::Current()->GetTaskRunners().GetUITaskRunner(), @@ -205,7 +205,7 @@ SceneHost::SceneHost(fml::RefPtr viewHolderToken, SceneHost::~SceneHost() { scene_host_bindings.erase(SceneHostBindingKey(koid_, isolate_service_id_)); - gpu_task_runner_->PostTask( + raster_task_runner_->PostTask( [id = koid_]() { flutter::ViewHolder::Destroy(id); }); } @@ -220,8 +220,9 @@ void SceneHost::setProperties(double width, double insetBottom, double insetLeft, bool focusable) { - gpu_task_runner_->PostTask([id = koid_, width, height, insetTop, insetRight, - insetBottom, insetLeft, focusable]() { + raster_task_runner_->PostTask([id = koid_, width, height, insetTop, + insetRight, insetBottom, insetLeft, + focusable]() { auto* view_holder = flutter::ViewHolder::FromId(id); FML_DCHECK(view_holder); diff --git a/lib/ui/compositing/scene_host.h b/lib/ui/compositing/scene_host.h index bf501a128ea2f..80f53866fe8a4 100644 --- a/lib/ui/compositing/scene_host.h +++ b/lib/ui/compositing/scene_host.h @@ -53,7 +53,7 @@ class SceneHost : public RefCountedDartWrappable { Dart_Handle viewDisconnectedCallback, Dart_Handle viewStateChangedCallback); - fml::RefPtr gpu_task_runner_; + fml::RefPtr raster_task_runner_; tonic::DartPersistentValue view_connected_callback_; tonic::DartPersistentValue view_disconnected_callback_; tonic::DartPersistentValue view_state_changed_callback_; diff --git a/lib/ui/painting/image_encoding.cc b/lib/ui/painting/image_encoding.cc index 6b56a74a36546..c82f623c00af1 100644 --- a/lib/ui/painting/image_encoding.cc +++ b/lib/ui/painting/image_encoding.cc @@ -83,7 +83,7 @@ sk_sp ConvertToRasterUsingResourceContext( void ConvertImageToRaster(sk_sp image, std::function)> encode_task, - fml::RefPtr gpu_task_runner, + fml::RefPtr raster_task_runner, fml::RefPtr io_task_runner, GrContext* resource_context, fml::WeakPtr snapshot_delegate) { @@ -118,9 +118,9 @@ void ConvertImageToRaster(sk_sp image, // Cross-context images do not support makeRasterImage. Convert these images // by drawing them into a surface. This must be done on the raster thread // to prevent concurrent usage of the image on both the IO and raster threads. - gpu_task_runner->PostTask([image, encode_task = std::move(encode_task), - resource_context, snapshot_delegate, - io_task_runner]() { + raster_task_runner->PostTask([image, encode_task = std::move(encode_task), + resource_context, snapshot_delegate, + io_task_runner]() { sk_sp raster_image = snapshot_delegate->ConvertToRasterImage(image); @@ -210,7 +210,7 @@ void EncodeImageAndInvokeDataCallback( std::unique_ptr callback, ImageByteFormat format, fml::RefPtr ui_task_runner, - fml::RefPtr gpu_task_runner, + fml::RefPtr raster_task_runner, fml::RefPtr io_task_runner, GrContext* resource_context, fml::WeakPtr snapshot_delegate) { @@ -227,7 +227,7 @@ void EncodeImageAndInvokeDataCallback( encoded = std::move(encoded)] { callback_task(encoded); }); }; - ConvertImageToRaster(std::move(image), encode_task, gpu_task_runner, + ConvertImageToRaster(std::move(image), encode_task, raster_task_runner, io_task_runner, resource_context, snapshot_delegate); } @@ -252,14 +252,14 @@ Dart_Handle EncodeImage(CanvasImage* canvas_image, task_runners.GetIOTaskRunner()->PostTask(fml::MakeCopyable( [callback = std::move(callback), image = canvas_image->image(), image_format, ui_task_runner = task_runners.GetUITaskRunner(), - gpu_task_runner = task_runners.GetGPUTaskRunner(), + raster_task_runner = task_runners.GetRasterTaskRunner(), io_task_runner = task_runners.GetIOTaskRunner(), io_manager = UIDartState::Current()->GetIOManager(), snapshot_delegate = UIDartState::Current()->GetSnapshotDelegate()]() mutable { EncodeImageAndInvokeDataCallback( std::move(image), std::move(callback), image_format, - std::move(ui_task_runner), std::move(gpu_task_runner), + std::move(ui_task_runner), std::move(raster_task_runner), std::move(io_task_runner), io_manager->GetResourceContext().get(), std::move(snapshot_delegate)); })); diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index 8527b83ec139b..e20f687d73754 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -79,7 +79,7 @@ Dart_Handle Picture::RasterizeToImage(sk_sp picture, new tonic::DartPersistentValue(dart_state, raw_image_callback); auto unref_queue = dart_state->GetSkiaUnrefQueue(); auto ui_task_runner = dart_state->GetTaskRunners().GetUITaskRunner(); - auto gpu_task_runner = dart_state->GetTaskRunners().GetGPUTaskRunner(); + auto raster_task_runner = dart_state->GetTaskRunners().GetRasterTaskRunner(); auto snapshot_delegate = dart_state->GetSnapshotDelegate(); // We can't create an image on this task runner because we don't have a @@ -117,7 +117,7 @@ Dart_Handle Picture::RasterizeToImage(sk_sp picture, // Kick things off on the GPU. fml::TaskRunner::RunNowOrPostTask( - gpu_task_runner, + raster_task_runner, [ui_task_runner, snapshot_delegate, picture, picture_bounds, ui_task] { sk_sp raster_image = snapshot_delegate->MakeRasterSnapshot(picture, picture_bounds); diff --git a/runtime/dart_isolate.cc b/runtime/dart_isolate.cc index 055d049c2640b..c8d7940c7baed 100644 --- a/runtime/dart_isolate.cc +++ b/runtime/dart_isolate.cc @@ -222,9 +222,9 @@ bool DartIsolate::UpdateThreadPoolNames() const { // shells sharing the same (or subset of) threads. const auto& task_runners = GetTaskRunners(); - if (auto task_runner = task_runners.GetGPUTaskRunner()) { + if (auto task_runner = task_runners.GetRasterTaskRunner()) { task_runner->PostTask( - [label = task_runners.GetLabel() + std::string{".gpu"}]() { + [label = task_runners.GetLabel() + std::string{".raster"}]() { Dart_SetThreadName(label.c_str()); }); } @@ -687,7 +687,7 @@ Dart_Isolate DartIsolate::DartIsolateGroupCreateCallback( parent_group_data.GetIsolateShutdownCallback()))); TaskRunners null_task_runners(advisory_script_uri, - /* platform= */ nullptr, /* gpu= */ nullptr, + /* platform= */ nullptr, /* raster= */ nullptr, /* ui= */ nullptr, /* io= */ nullptr); @@ -729,7 +729,7 @@ bool DartIsolate::DartIsolateInitializeCallback(void** child_callback_data, Dart_CurrentIsolateGroupData()); TaskRunners null_task_runners((*isolate_group_data)->GetAdvisoryScriptURI(), - /* platform= */ nullptr, /* gpu= */ nullptr, + /* platform= */ nullptr, /* raster= */ nullptr, /* ui= */ nullptr, /* io= */ nullptr); diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 626e557c61b02..322411a11b269 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -36,7 +36,7 @@ Animator::Animator(Delegate& delegate, // https://github.com/flutter/engine/pull/9132 for discussion. layer_tree_pipeline_(fml::MakeRefCounted( task_runners.GetPlatformTaskRunner() == - task_runners.GetGPUTaskRunner() + task_runners.GetRasterTaskRunner() ? 1 : 2)), #endif // FLUTTER_SHELL_ENABLE_METAL diff --git a/shell/common/platform_view.cc b/shell/common/platform_view.cc index f96dc0a945994..9fa42ae17d22e 100644 --- a/shell/common/platform_view.cc +++ b/shell/common/platform_view.cc @@ -69,7 +69,7 @@ void PlatformView::NotifyCreated() { auto* platform_view = this; fml::ManualResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetGPUTaskRunner(), [platform_view, &surface, &latch]() { + task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch]() { surface = platform_view->CreateRenderingSurface(); latch.Signal(); }); diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f7e4350fe9c97..4d7d5ea669b2f 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -69,9 +69,9 @@ void Rasterizer::Setup(std::unique_ptr surface) { if (surface_->GetExternalViewEmbedder()) { const auto platform_id = task_runners_.GetPlatformTaskRunner()->GetTaskQueueId(); - const auto gpu_id = task_runners_.GetGPUTaskRunner()->GetTaskQueueId(); - gpu_thread_merger_ = - fml::MakeRefCounted(platform_id, gpu_id); + const auto gpu_id = task_runners_.GetRasterTaskRunner()->GetTaskQueueId(); + raster_thread_merger_ = + fml::MakeRefCounted(platform_id, gpu_id); } } @@ -111,11 +111,12 @@ void Rasterizer::DrawLastLayerTree() { void Rasterizer::Draw(fml::RefPtr> pipeline) { TRACE_EVENT0("flutter", "GPURasterizer::Draw"); - if (gpu_thread_merger_ && !gpu_thread_merger_->IsOnRasterizingThread()) { + if (raster_thread_merger_ && + !raster_thread_merger_->IsOnRasterizingThread()) { // we yield and let this frame be serviced on the right thread. return; } - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); RasterStatus raster_status = RasterStatus::kFailed; Pipeline::Consumer consumer = @@ -137,7 +138,7 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { // between successive tries. switch (consume_result) { case PipelineConsumeResult::MoreAvailable: { - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [weak_this = weak_factory_.GetWeakPtr(), pipeline]() { if (weak_this) { weak_this->Draw(pipeline); @@ -232,7 +233,7 @@ sk_sp Rasterizer::ConvertToRasterImage(sk_sp image) { RasterStatus Rasterizer::DoDraw( std::unique_ptr layer_tree) { - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); if (!layer_tree || !surface_) { return RasterStatus::kFailed; @@ -283,9 +284,9 @@ RasterStatus Rasterizer::DoDraw( // - |Draw| for B yields as its on the wrong thread. // This enqueue ensures that we attempt to consume from the right // thread one more time after un-merge. - if (gpu_thread_merger_) { - if (gpu_thread_merger_->DecrementLease() == - fml::GpuThreadStatus::kUnmergedNow) { + if (raster_thread_merger_) { + if (raster_thread_merger_->DecrementLease() == + fml::RasterThreadStatus::kUnmergedNow) { return RasterStatus::kEnqueuePipeline; } } @@ -334,7 +335,7 @@ RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { root_surface_transformation, // root surface transformation true, // instrumentation enabled frame->supports_readback(), // surface supports pixel reads - gpu_thread_merger_ // thread merger + raster_thread_merger_ // thread merger ); if (compositor_frame) { diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 800446c097605..f6bdd297ed8cc 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -13,8 +13,8 @@ #include "flutter/flow/compositor_context.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/fml/closure.h" -#include "flutter/fml/gpu_thread_merger.h" #include "flutter/fml/memory/weak_ptr.h" +#include "flutter/fml/raster_thread_merger.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/shell/common/pipeline.h" @@ -420,7 +420,7 @@ class Rasterizer final : public SnapshotDelegate { bool user_override_resource_cache_bytes_; std::optional max_cache_bytes_; fml::WeakPtrFactory weak_factory_; - fml::RefPtr gpu_thread_merger_; + fml::RefPtr raster_thread_merger_; // |SnapshotDelegate| sk_sp MakeRasterSnapshot(sk_sp picture, diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 1b8dbb6d2f6ff..e660c54ede6c3 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -62,10 +62,10 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( std::promise> snapshot_delegate_promise; auto snapshot_delegate_future = snapshot_delegate_promise.get_future(); fml::TaskRunner::RunNowOrPostTask( - task_runners.GetGPUTaskRunner(), [&rasterizer_promise, // - &snapshot_delegate_promise, - on_create_rasterizer, // - shell = shell.get() // + task_runners.GetRasterTaskRunner(), [&rasterizer_promise, // + &snapshot_delegate_promise, + on_create_rasterizer, // + shell = shell.get() // ]() { TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem"); std::unique_ptr rasterizer(on_create_rasterizer(*shell)); @@ -340,7 +340,7 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) // need to wait on a latch because it can only ever be used from the raster // thread from this class, so we have ordering guarantees. fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetGPUTaskRunner(), fml::MakeCopyable([this]() mutable { + task_runners_.GetRasterTaskRunner(), fml::MakeCopyable([this]() mutable { this->weak_factory_gpu_ = std::make_unique>(this); })); @@ -348,11 +348,11 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) // Install service protocol handlers. service_protocol_handlers_[ServiceProtocol::kScreenshotExtensionName] = { - task_runners_.GetGPUTaskRunner(), + task_runners_.GetRasterTaskRunner(), std::bind(&Shell::OnServiceProtocolScreenshot, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[ServiceProtocol::kScreenshotSkpExtensionName] = { - task_runners_.GetGPUTaskRunner(), + task_runners_.GetRasterTaskRunner(), std::bind(&Shell::OnServiceProtocolScreenshotSKP, this, std::placeholders::_1, std::placeholders::_2)}; service_protocol_handlers_[ServiceProtocol::kRunInViewExtensionName] = { @@ -397,7 +397,7 @@ Shell::~Shell() { ui_latch.Wait(); fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetGPUTaskRunner(), + task_runners_.GetRasterTaskRunner(), fml::MakeCopyable([rasterizer = std::move(rasterizer_), weak_factory_gpu = std::move(weak_factory_gpu_), &gpu_latch]() mutable { @@ -441,7 +441,7 @@ void Shell::NotifyLowMemoryWarning() const { // running. ::Dart_NotifyLowMemory(); - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr()]() { if (rasterizer) { rasterizer->NotifyLowMemoryWarning(); @@ -626,15 +626,15 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { // gpu_task to the raster thread which signals the latch. If the GPU the and // platform threads are the same this results in a deadlock as the gpu_task // will never be posted to the plaform/raster thread that is blocked on a - // latch. To avoid the described deadlock, if the gpu and the platform threads - // are the same, should_post_gpu_task will be false, and then instead of - // posting a task to the raster thread, the ui thread just signals the latch - // and the platform/raster thread follows with executing gpu_task. - bool should_post_gpu_task = - task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); - - auto ui_task = [engine = engine_->GetWeakPtr(), // - gpu_task_runner = task_runners_.GetGPUTaskRunner(), // + // latch. To avoid the described deadlock, if the raster and the platform + // threads are the same, should_post_gpu_task will be false, and then instead + // of posting a task to the raster thread, the ui thread just signals the + // latch and the platform/raster thread follows with executing gpu_task. + bool should_post_gpu_task = task_runners_.GetRasterTaskRunner() != + task_runners_.GetPlatformTaskRunner(); + + auto ui_task = [engine = engine_->GetWeakPtr(), // + raster_task_runner = task_runners_.GetRasterTaskRunner(), // gpu_task, should_post_gpu_task, &latch // ] { @@ -644,7 +644,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { // Step 2: Next, tell the raster thread that it should create a surface for // its rasterizer. if (should_post_gpu_task) { - fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + fml::TaskRunner::RunNowOrPostTask(raster_task_runner, gpu_task); } else { // See comment on should_post_gpu_task, in this case we just unblock // the platform thread. @@ -721,24 +721,24 @@ void Shell::OnPlatformViewDestroyed() { // gpu_task to the raster thread triggers signaling the latch(on the IO // thread). If the GPU the and platform threads are the same this results in a // deadlock as the gpu_task will never be posted to the plaform/raster thread - // that is blocked on a latch. To avoid the described deadlock, if the gpu + // that is blocked on a latch. To avoid the described deadlock, if the raster // and the platform threads are the same, should_post_gpu_task will be false, // and then instead of posting a task to the raster thread, the ui thread just // signals the latch and the platform/raster thread follows with executing // gpu_task. - bool should_post_gpu_task = - task_runners_.GetGPUTaskRunner() != task_runners_.GetPlatformTaskRunner(); + bool should_post_gpu_task = task_runners_.GetRasterTaskRunner() != + task_runners_.GetPlatformTaskRunner(); auto ui_task = [engine = engine_->GetWeakPtr(), - gpu_task_runner = task_runners_.GetGPUTaskRunner(), gpu_task, - should_post_gpu_task, &latch]() { + raster_task_runner = task_runners_.GetRasterTaskRunner(), + gpu_task, should_post_gpu_task, &latch]() { if (engine) { engine->OnOutputSurfaceDestroyed(); } // Step 1: Next, tell the raster thread that its rasterizer should suspend // access to the underlying surface. if (should_post_gpu_task) { - fml::TaskRunner::RunNowOrPostTask(gpu_task_runner, gpu_task); + fml::TaskRunner::RunNowOrPostTask(raster_task_runner, gpu_task); } else { // See comment on should_post_gpu_task, in this case we just unblock // the platform thread. @@ -767,7 +767,7 @@ void Shell::OnPlatformViewSetViewportMetrics(const ViewportMetrics& metrics) { // This is the formula Android uses. // https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/renderthread/CacheManager.cpp#41 size_t max_bytes = metrics.physical_width * metrics.physical_height * 12 * 4; - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), max_bytes] { if (rasterizer) { rasterizer->SetResourceCacheMaxBytes(max_bytes, false); @@ -860,7 +860,7 @@ void Shell::OnPlatformViewRegisterTexture( FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture] { if (rasterizer) { if (auto* registry = rasterizer->GetTextureRegistry()) { @@ -875,7 +875,7 @@ void Shell::OnPlatformViewUnregisterTexture(int64_t texture_id) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { if (rasterizer) { if (auto* registry = rasterizer->GetTextureRegistry()) { @@ -891,7 +891,7 @@ void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) { FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); // Tell the rasterizer that one of its textures has a new frame available. - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), texture_id]() { auto* registry = rasterizer->GetTextureRegistry(); @@ -921,7 +921,7 @@ void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), closure = closure]() { if (rasterizer) { rasterizer->SetNextFrameCallback(std::move(closure)); @@ -953,7 +953,7 @@ void Shell::OnAnimatorNotifyIdle(int64_t deadline) { void Shell::OnAnimatorDraw(fml::RefPtr> pipeline) { FML_DCHECK(is_setup_); - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [& waiting_for_first_frame = waiting_for_first_frame_, &waiting_for_first_frame_condition = waiting_for_first_frame_condition_, rasterizer = rasterizer_->GetWeakPtr(), @@ -973,7 +973,7 @@ void Shell::OnAnimatorDraw(fml::RefPtr> pipeline) { void Shell::OnAnimatorDrawLastLayerTree() { FML_DCHECK(is_setup_); - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr()]() { if (rasterizer) { rasterizer->DrawLastLayerTree(); @@ -1030,7 +1030,7 @@ void Shell::HandleEngineSkiaMessage(fml::RefPtr message) { if (args == root.MemberEnd() || !args->value.IsInt()) return; - task_runners_.GetGPUTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask( [rasterizer = rasterizer_->GetWeakPtr(), max_bytes = args->value.GetInt(), response = std::move(message->response())] { if (rasterizer) { @@ -1079,7 +1079,7 @@ void Shell::SetNeedsReportTimings(bool value) { void Shell::ReportTimings() { FML_DCHECK(is_setup_); - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); auto timings = std::move(unreported_timings_); unreported_timings_ = {}; @@ -1092,14 +1092,14 @@ void Shell::ReportTimings() { size_t Shell::UnreportedFramesCount() const { // Check that this is running on the raster thread to avoid race conditions. - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); FML_DCHECK(unreported_timings_.size() % FrameTiming::kCount == 0); return unreported_timings_.size() / FrameTiming::kCount; } void Shell::OnFrameRasterized(const FrameTiming& timing) { FML_DCHECK(is_setup_); - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); // The C++ callback defined in settings.h and set by Flutter runner. This is // independent of the timings report to the Dart side. @@ -1142,7 +1142,7 @@ void Shell::OnFrameRasterized(const FrameTiming& timing) { // second. Otherwise, the timings of last few frames of an animation may // never be reported until the next animation starts. frame_timings_report_scheduled_ = true; - task_runners_.GetGPUTaskRunner()->PostDelayedTask( + task_runners_.GetRasterTaskRunner()->PostDelayedTask( [self = weak_factory_gpu_->GetWeakPtr()]() { if (!self.get()) { return; @@ -1223,7 +1223,7 @@ static void ServiceProtocolFailureError(rapidjson::Document& response, bool Shell::OnServiceProtocolScreenshot( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); auto screenshot = rasterizer_->ScreenshotLastLayerTree( Rasterizer::ScreenshotType::CompressedImage, true); if (screenshot.data) { @@ -1244,7 +1244,7 @@ bool Shell::OnServiceProtocolScreenshot( bool Shell::OnServiceProtocolScreenshotSKP( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document& response) { - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); auto screenshot = rasterizer_->ScreenshotLastLayerTree( Rasterizer::ScreenshotType::SkiaPicture, true); if (screenshot.data) { @@ -1418,11 +1418,11 @@ Rasterizer::Screenshot Shell::Screenshot( fml::AutoResetWaitableEvent latch; Rasterizer::Screenshot screenshot; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetGPUTaskRunner(), [&latch, // - rasterizer = GetRasterizer(), // - &screenshot, // - screenshot_type, // - base64_encode // + task_runners_.GetRasterTaskRunner(), [&latch, // + rasterizer = GetRasterizer(), // + &screenshot, // + screenshot_type, // + base64_encode // ]() { if (rasterizer) { screenshot = rasterizer->ScreenshotLastLayerTree(screenshot_type, @@ -1437,7 +1437,7 @@ Rasterizer::Screenshot Shell::Screenshot( fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) { FML_DCHECK(is_setup_); if (task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread() || - task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()) { + task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()) { return fml::Status(fml::StatusCode::kFailedPrecondition, "WaitForFirstFrame called from thread that can't wait " "because it is responsible for generating the frame."); diff --git a/shell/common/shell_benchmarks.cc b/shell/common/shell_benchmarks.cc index 99d38e70d8143..c5b2751204ac4 100644 --- a/shell/common/shell_benchmarks.cc +++ b/shell/common/shell_benchmarks.cc @@ -48,7 +48,7 @@ static void StartupAndShutdownShell(benchmark::State& state, TaskRunners task_runners("test", thread_host->platform_thread->GetTaskRunner(), - thread_host->gpu_thread->GetTaskRunner(), + thread_host->raster_thread->GetTaskRunner(), thread_host->ui_thread->GetTaskRunner(), thread_host->io_thread->GetTaskRunner()); diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 831ab18394a53..5522feb591f79 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -244,7 +244,7 @@ TaskRunners ShellTest::GetTaskRunnersForFixture() { return { "test", thread_host_.platform_thread->GetTaskRunner(), // platform - thread_host_.gpu_thread->GetTaskRunner(), // raster + thread_host_.raster_thread->GetTaskRunner(), // raster thread_host_.ui_thread->GetTaskRunner(), // ui thread_host_.io_thread->GetTaskRunner() // io }; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 21787017d2d02..96308c408b951 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -75,7 +75,7 @@ TEST_F(ShellTest, InitializeWithDifferentThreads) { ThreadHost::Type::Platform | ThreadHost::Type::GPU | ThreadHost::Type::IO | ThreadHost::Type::UI); TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), - thread_host.gpu_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner()); auto shell = CreateShell(std::move(settings), std::move(task_runners)); @@ -124,7 +124,7 @@ TEST_F(ShellTest, fml::MessageLoop::EnsureInitializedForCurrentThread(); TaskRunners task_runners("test", fml::MessageLoop::GetCurrent().GetTaskRunner(), - thread_host.gpu_thread->GetTaskRunner(), + thread_host.raster_thread->GetTaskRunner(), thread_host.ui_thread->GetTaskRunner(), thread_host.io_thread->GetTaskRunner()); auto shell = Shell::Create( @@ -717,7 +717,7 @@ static size_t GetRasterizerResourceCacheBytesSync(Shell& shell) { size_t bytes = 0; fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - shell.GetTaskRunners().GetGPUTaskRunner(), [&]() { + shell.GetTaskRunners().GetRasterTaskRunner(), [&]() { if (auto rasterizer = shell.GetRasterizer()) { bytes = rasterizer->GetResourceCacheMaxBytes().value_or(0U); } @@ -938,7 +938,7 @@ TEST_F(ShellTest, TextureFrameMarkedAvailableAndUnregister) { std::make_shared(0, latch); fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetGPUTaskRunner(), [&]() { + shell->GetTaskRunners().GetRasterTaskRunner(), [&]() { shell->GetPlatformView()->RegisterTexture(mockTexture); shell->GetPlatformView()->MarkTextureFrameAvailable(0); }); @@ -947,7 +947,7 @@ TEST_F(ShellTest, TextureFrameMarkedAvailableAndUnregister) { EXPECT_EQ(mockTexture->frames_available(), 1); fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetGPUTaskRunner(), + shell->GetTaskRunners().GetRasterTaskRunner(), [&]() { shell->GetPlatformView()->UnregisterTexture(0); }); latch->Wait(); @@ -1032,7 +1032,7 @@ TEST_F(ShellTest, Screenshot) { auto screenshot_future = screenshot_promise.get_future(); fml::TaskRunner::RunNowOrPostTask( - shell->GetTaskRunners().GetGPUTaskRunner(), + shell->GetTaskRunners().GetRasterTaskRunner(), [&screenshot_promise, &shell]() { auto rasterizer = shell->GetRasterizer(); screenshot_promise.set_value(rasterizer->ScreenshotLastLayerTree( diff --git a/shell/common/thread_host.cc b/shell/common/thread_host.cc index 0ab677a76b7bd..5e2201e097a86 100644 --- a/shell/common/thread_host.cc +++ b/shell/common/thread_host.cc @@ -20,7 +20,7 @@ ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) { } if (mask & ThreadHost::Type::GPU) { - gpu_thread = std::make_unique(name_prefix + ".gpu"); + raster_thread = std::make_unique(name_prefix + ".raster"); } if (mask & ThreadHost::Type::IO) { @@ -33,7 +33,7 @@ ThreadHost::~ThreadHost() = default; void ThreadHost::Reset() { platform_thread.reset(); ui_thread.reset(); - gpu_thread.reset(); + raster_thread.reset(); io_thread.reset(); } diff --git a/shell/common/thread_host.h b/shell/common/thread_host.h index b5e14e6fa6bf8..9f2643ed7e4f0 100644 --- a/shell/common/thread_host.h +++ b/shell/common/thread_host.h @@ -23,7 +23,7 @@ struct ThreadHost { std::unique_ptr platform_thread; std::unique_ptr ui_thread; - std::unique_ptr gpu_thread; + std::unique_ptr raster_thread; std::unique_ptr io_thread; ThreadHost(); diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index 1274969ec8b4e..fe4d85ab7b259 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -51,7 +51,7 @@ AndroidShellHolder::AndroidShellHolder( }); thread_host_.ui_thread->GetTaskRunner()->PostTask(jni_exit_task); if (!is_background_view) { - thread_host_.gpu_thread->GetTaskRunner()->PostTask(jni_exit_task); + thread_host_.raster_thread->GetTaskRunner()->PostTask(jni_exit_task); } fml::WeakPtr weak_platform_view; @@ -96,7 +96,7 @@ AndroidShellHolder::AndroidShellHolder( ui_runner = single_task_runner; io_runner = single_task_runner; } else { - gpu_runner = thread_host_.gpu_thread->GetTaskRunner(); + gpu_runner = thread_host_.raster_thread->GetTaskRunner(); ui_runner = thread_host_.ui_thread->GetTaskRunner(); io_runner = thread_host_.io_thread->GetTaskRunner(); } @@ -121,7 +121,7 @@ AndroidShellHolder::AndroidShellHolder( is_valid_ = shell_ != nullptr; if (is_valid_) { - task_runners.GetGPUTaskRunner()->PostTask([]() { + task_runners.GetRasterTaskRunner()->PostTask([]() { // Android describes -8 as "most important display threads, for // compositing the screen and retrieving input events". Conservatively // set the raster thread to slightly lower priority than it. diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index 856fd9748ce90..a58efec690e37 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -48,7 +48,7 @@ void PlatformViewAndroid::NotifyCreated( fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetGPUTaskRunner(), + task_runners_.GetRasterTaskRunner(), [&latch, surface = android_surface_.get(), native_window = std::move(native_window)]() { surface->SetNativeWindow(native_window); @@ -66,7 +66,7 @@ void PlatformViewAndroid::NotifyDestroyed() { if (android_surface_) { fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetGPUTaskRunner(), + task_runners_.GetRasterTaskRunner(), [&latch, surface = android_surface_.get()]() { surface->TeardownOnScreenContext(); latch.Signal(); @@ -81,7 +81,7 @@ void PlatformViewAndroid::NotifyChanged(const SkISize& size) { } fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask( - task_runners_.GetGPUTaskRunner(), // + task_runners_.GetRasterTaskRunner(), // [&latch, surface = android_surface_.get(), size]() { surface->OnScreenSurfaceResize(size); latch.Signal(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 354d482c283ae..cf636aef8a3ea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -195,9 +195,9 @@ - (void)dispatchPointerDataPacket:(std::unique_ptr)p return _shell->GetTaskRunners().GetPlatformTaskRunner(); } -- (fml::RefPtr)GPUTaskRunner { +- (fml::RefPtr)RasterTaskRunner { FML_DCHECK(_shell); - return _shell->GetTaskRunners().GetGPUTaskRunner(); + return _shell->GetTaskRunners().GetRasterTaskRunner(); } - (void)ensureSemanticsEnabled { @@ -476,7 +476,7 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { } else { flutter::TaskRunners task_runners(threadLabel.UTF8String, // label fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform - _threadHost.gpu_thread->GetTaskRunner(), // raster + _threadHost.raster_thread->GetTaskRunner(), // raster _threadHost.ui_thread->GetTaskRunner(), // ui _threadHost.io_thread->GetTaskRunner() // io ); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h index 277950f9d68d0..e995b6f77f9c9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h @@ -32,7 +32,7 @@ - (void)dispatchPointerDataPacket:(std::unique_ptr)packet; - (fml::RefPtr)platformTaskRunner; -- (fml::RefPtr)GPUTaskRunner; +- (fml::RefPtr)RasterTaskRunner; - (fml::WeakPtr)platformView; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index c2cbef18cc167..3fbaa7dd8a935 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -261,14 +261,14 @@ const int FlutterPlatformViewsController::kDefaultMergedLeaseDuration; PostPrerollResult FlutterPlatformViewsController::PostPrerollAction( - fml::RefPtr gpu_thread_merger) { + fml::RefPtr raster_thread_merger) { const bool uiviews_mutated = HasPendingViewOperations(); if (uiviews_mutated) { - if (gpu_thread_merger->IsMerged()) { - gpu_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); + if (raster_thread_merger->IsMerged()) { + raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); } else { CancelFrame(); - gpu_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); return PostPrerollResult::kResubmitFrame; } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index c5ebd9a479b1f..e998640777c96 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -146,7 +146,7 @@ class FlutterPlatformViewsController { // returns nil. NSObject* GetPlatformViewByID(int view_id); - PostPrerollResult PostPrerollAction(fml::RefPtr gpu_thread_merger); + PostPrerollResult PostPrerollAction(fml::RefPtr raster_thread_merger); std::vector GetCurrentCanvases(); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index e1f76da1d5df4..889279d0e9ee6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -451,8 +451,8 @@ - (void)installFirstFrameCallback { // Start on the platform thread. weakPlatformView->SetNextFrameCallback([weakSelf = [self getWeakPtr], platformTaskRunner = [_engine.get() platformTaskRunner], - gpuTaskRunner = [_engine.get() GPUTaskRunner]]() { - FML_DCHECK(gpuTaskRunner->RunsTasksOnCurrentThread()); + RasterTaskRunner = [_engine.get() RasterTaskRunner]]() { + FML_DCHECK(RasterTaskRunner->RunsTasksOnCurrentThread()); // Get callback on raster thread and jump back to platform thread. platformTaskRunner->PostTask([weakSelf]() { fml::scoped_nsobject flutterViewController( diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index 8cba9285cfb02..fd1bc7abb0b48 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -68,7 +68,8 @@ class IOSSurface : public ExternalViewEmbedder { std::unique_ptr params) override; // |ExternalViewEmbedder| - PostPrerollResult PostPrerollAction(fml::RefPtr gpu_thread_merger) override; + PostPrerollResult PostPrerollAction( + fml::RefPtr raster_thread_merger) override; // |ExternalViewEmbedder| std::vector GetCurrentCanvases() override; diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index f51160e94e471..cbe2cb4fb8c2c 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -112,10 +112,10 @@ bool IsIosEmbeddedViewsPreviewEnabled() { // |ExternalViewEmbedder| PostPrerollResult IOSSurface::PostPrerollAction( - fml::RefPtr gpu_thread_merger) { + fml::RefPtr raster_thread_merger) { TRACE_EVENT0("flutter", "IOSSurface::PostPrerollAction"); FML_CHECK(platform_views_controller_ != nullptr); - return platform_views_controller_->PostPrerollAction(gpu_thread_merger); + return platform_views_controller_->PostPrerollAction(raster_thread_merger); } // |ExternalViewEmbedder| diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 53b70968b341d..ff5118207cfaf 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -94,7 +94,7 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), // |PlatformView| std::unique_ptr PlatformViewIOS::CreateRenderingSurface() { - FML_DCHECK(task_runners_.GetGPUTaskRunner()->RunsTasksOnCurrentThread()); + FML_DCHECK(task_runners_.GetRasterTaskRunner()->RunsTasksOnCurrentThread()); std::lock_guard guard(ios_surface_mutex_); if (!ios_surface_) { FML_DLOG(INFO) << "Could not CreateRenderingSurface, this PlatformViewIOS " diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 23202052f2096..7d6f2c5d788f1 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -232,7 +232,7 @@ bool EmbedderEngine::PostRenderThreadTask(const fml::closure& task) { return false; } - shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask(task); + shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask(task); return true; } @@ -260,7 +260,8 @@ bool EmbedderEngine::PostTaskOnEngineManagedNativeThreads( // Post the task to all thread host threads. const auto& task_runners = shell_->GetTaskRunners(); - trampoline(kFlutterNativeThreadTypeRender, task_runners.GetGPUTaskRunner()); + trampoline(kFlutterNativeThreadTypeRender, + task_runners.GetRasterTaskRunner()); trampoline(kFlutterNativeThreadTypeWorker, task_runners.GetIOTaskRunner()); trampoline(kFlutterNativeThreadTypeUI, task_runners.GetUITaskRunner()); trampoline(kFlutterNativeThreadTypePlatform, diff --git a/shell/platform/embedder/embedder_thread_host.cc b/shell/platform/embedder/embedder_thread_host.cc index 430434bb15286..83860eb635d56 100644 --- a/shell/platform/embedder/embedder_thread_host.cc +++ b/shell/platform/embedder/embedder_thread_host.cc @@ -168,7 +168,7 @@ EmbedderThreadHost::CreateEmbedderManagedThreadHost( auto render_task_runner = render_task_runner_pair.second ? static_cast>( render_task_runner_pair.second) - : thread_host.gpu_thread->GetTaskRunner(); + : thread_host.raster_thread->GetTaskRunner(); flutter::TaskRunners task_runners( kFlutterThreadName, @@ -219,10 +219,10 @@ EmbedderThreadHost::CreateEngineManagedThreadHost() { flutter::TaskRunners task_runners( kFlutterThreadName, - platform_task_runner, // platform - thread_host.gpu_thread->GetTaskRunner(), // raster - thread_host.ui_thread->GetTaskRunner(), // ui - thread_host.io_thread->GetTaskRunner() // io + platform_task_runner, // platform + thread_host.raster_thread->GetTaskRunner(), // raster + thread_host.ui_thread->GetTaskRunner(), // ui + thread_host.io_thread->GetTaskRunner() // io ); if (!task_runners.IsValid()) { diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 3164fd712b76a..291d8cd266163 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -754,7 +754,7 @@ TEST_F(EmbedderTest, RasterCacheDisabledWithPlatformViews) { setup.Wait(); const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell(); - shell.GetTaskRunners().GetGPUTaskRunner()->PostTask([&] { + shell.GetTaskRunners().GetRasterTaskRunner()->PostTask([&] { const flutter::RasterCache& raster_cache = shell.GetRasterizer()->compositor_context()->raster_cache(); // 3 layers total, but one of them had the platform view. So the cache @@ -826,7 +826,7 @@ TEST_F(EmbedderTest, RasterCacheEnabled) { setup.Wait(); const flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell(); - shell.GetTaskRunners().GetGPUTaskRunner()->PostTask([&] { + shell.GetTaskRunners().GetRasterTaskRunner()->PostTask([&] { const flutter::RasterCache& raster_cache = shell.GetRasterizer()->compositor_context()->raster_cache(); ASSERT_EQ(raster_cache.GetCachedEntriesCount(), 1u); diff --git a/shell/platform/fuchsia/flutter/compositor_context.cc b/shell/platform/fuchsia/flutter/compositor_context.cc index 556e80e73a66b..91513883ffcf4 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.cc +++ b/shell/platform/fuchsia/flutter/compositor_context.cc @@ -103,7 +103,7 @@ CompositorContext::AcquireFrame( const SkMatrix& root_surface_transformation, bool instrumentation_enabled, bool surface_supports_readback, - fml::RefPtr gpu_thread_merger) { + fml::RefPtr raster_thread_merger) { // TODO: The AcquireFrame interface is too broad and must be refactored to get // rid of the context and canvas arguments as those seem to be only used for // colorspace correctness purposes on the mobile shells. diff --git a/shell/platform/fuchsia/flutter/compositor_context.h b/shell/platform/fuchsia/flutter/compositor_context.h index 46088f8e6fe36..ced422692ecfd 100644 --- a/shell/platform/fuchsia/flutter/compositor_context.h +++ b/shell/platform/fuchsia/flutter/compositor_context.h @@ -49,7 +49,7 @@ class CompositorContext final : public flutter::CompositorContext { const SkMatrix& root_surface_transformation, bool instrumentation_enabled, bool surface_supports_readback, - fml::RefPtr gpu_thread_merger) override; + fml::RefPtr raster_thread_merger) override; FML_DISALLOW_COPY_AND_ASSIGN(CompositorContext); }; diff --git a/shell/platform/fuchsia/flutter/engine.cc b/shell/platform/fuchsia/flutter/engine.cc index 1b79dd6534fbc..66f4c403037d7 100644 --- a/shell/platform/fuchsia/flutter/engine.cc +++ b/shell/platform/fuchsia/flutter/engine.cc @@ -38,7 +38,7 @@ static void UpdateNativeThreadLabelNames(const std::string& label, }; set_thread_name(runners.GetPlatformTaskRunner(), label, ".platform"); set_thread_name(runners.GetUITaskRunner(), label, ".ui"); - set_thread_name(runners.GetGPUTaskRunner(), label, ".gpu"); + set_thread_name(runners.GetRasterTaskRunner(), label, ".raster"); set_thread_name(runners.GetIOTaskRunner(), label, ".io"); } @@ -474,7 +474,7 @@ void Engine::OnSessionMetricsDidChange( return; } - shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask( + shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( [rasterizer = shell_->GetRasterizer(), metrics]() { if (rasterizer) { auto compositor_context = @@ -491,7 +491,7 @@ void Engine::OnDebugWireframeSettingsChanged(bool enabled) { return; } - shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask( + shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( [rasterizer = shell_->GetRasterizer(), enabled]() { if (rasterizer) { auto compositor_context = @@ -509,7 +509,7 @@ void Engine::OnSessionSizeChangeHint(float width_change_factor, return; } - shell_->GetTaskRunners().GetGPUTaskRunner()->PostTask( + shell_->GetTaskRunners().GetRasterTaskRunner()->PostTask( [rasterizer = shell_->GetRasterizer(), width_change_factor, height_change_factor]() { if (rasterizer) { diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index ce0be3b012c9b..cab64817ae241 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -109,7 +109,7 @@ int RunTester(const flutter::Settings& settings, std::unique_ptr threadhost; fml::RefPtr platform_task_runner; - fml::RefPtr gpu_task_runner; + fml::RefPtr raster_task_runner; fml::RefPtr ui_task_runner; fml::RefPtr io_task_runner; @@ -118,17 +118,17 @@ int RunTester(const flutter::Settings& settings, thread_label, ThreadHost::Type::Platform | ThreadHost::Type::IO | ThreadHost::Type::UI | ThreadHost::Type::GPU); platform_task_runner = current_task_runner; - gpu_task_runner = threadhost->gpu_thread->GetTaskRunner(); + raster_task_runner = threadhost->raster_thread->GetTaskRunner(); ui_task_runner = threadhost->ui_thread->GetTaskRunner(); io_task_runner = threadhost->io_thread->GetTaskRunner(); } else { - platform_task_runner = gpu_task_runner = ui_task_runner = io_task_runner = - current_task_runner; + platform_task_runner = raster_task_runner = ui_task_runner = + io_task_runner = current_task_runner; } const flutter::TaskRunners task_runners(thread_label, // dart thread label platform_task_runner, // platform - gpu_task_runner, // raster + raster_task_runner, // raster ui_task_runner, // ui io_task_runner // io ); From 70b12fa181723aa1bc4a8bef26164226e686a260 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 31 Mar 2020 15:02:26 -0700 Subject: [PATCH 197/386] Strengthen json string comparisons (#17425) Make the test robust even if the json map reorders its keys. --- shell/common/shell_unittests.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 96308c408b951..8a7bd5d51dcea 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1162,10 +1162,14 @@ TEST_F(ShellTest, OnServiceProtocolGetSkSLsWorks) { document.Accept(writer); DestroyShell(std::move(shell)); - // Base64 encoding of x, y are eQ, eA. - ASSERT_STREQ( - buffer.GetString(), - "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"II\":\"eQ==\",\"IE\":\"eA==\"}}"); + const std::string expected_json1 = + "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"II\":\"eQ==\",\"IE\":\"eA==\"}}"; + const std::string expected_json2 = + "{\"type\":\"GetSkSLs\",\"SkSLs\":{\"IE\":\"eA==\",\"II\":\"eQ==\"}}"; + bool json_is_expected = (expected_json1 == buffer.GetString()) || + (expected_json2 == buffer.GetString()); + ASSERT_TRUE(json_is_expected) << buffer.GetString() << " is not equal to " + << expected_json1 << " or " << expected_json2; // Cleanup files fml::FileVisitor recursive_cleanup = [&recursive_cleanup]( From 7fc22d118e177a959f211dfe939379b14351a49c Mon Sep 17 00:00:00 2001 From: Nathan Rogers Date: Tue, 31 Mar 2020 15:09:38 -0700 Subject: [PATCH 198/386] [fuchsia] Remove "CreateTraceProvider" trace event (#17423) Since the trace provider hasn't been created yet, it is not possible to trace the creation of a trace provider using a TRACE_DURATION event (the trace enabled and category check will always fail). While this isn't causing any urgent problems, remove it to set a good example for other clients of tracing that might happen to reference this code. --- shell/platform/fuchsia/dart_runner/main.cc | 1 - shell/platform/fuchsia/flutter/main.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/shell/platform/fuchsia/dart_runner/main.cc b/shell/platform/fuchsia/dart_runner/main.cc index 357ac33324c5d..2dfe8d418106b 100644 --- a/shell/platform/fuchsia/dart_runner/main.cc +++ b/shell/platform/fuchsia/dart_runner/main.cc @@ -36,7 +36,6 @@ int main(int argc, const char** argv) { std::unique_ptr provider; { - TRACE_DURATION("dart", "CreateTraceProvider"); bool already_started; // Use CreateSynchronously to prevent loss of early events. trace::TraceProviderWithFdio::CreateSynchronously( diff --git a/shell/platform/fuchsia/flutter/main.cc b/shell/platform/fuchsia/flutter/main.cc index e37a12d275634..8fa2dfe803a7b 100644 --- a/shell/platform/fuchsia/flutter/main.cc +++ b/shell/platform/fuchsia/flutter/main.cc @@ -17,7 +17,6 @@ int main(int argc, char const* argv[]) { std::unique_ptr provider; { - TRACE_DURATION("flutter", "CreateTraceProvider"); bool already_started; // Use CreateSynchronously to prevent loss of early events. trace::TraceProviderWithFdio::CreateSynchronously( From 6bdbe167601dabb13cee437cb11c20abbe306b83 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 31 Mar 2020 15:10:05 -0700 Subject: [PATCH 199/386] Fix iOS builds on Xcode 11.4 for new enum values added in iOS 13.4. (#17429) The following issues have been filed to track the handling of these enum values: * Handle the UITouchTypeIndirectPointer enum value. https://github.com/flutter/flutter/issues/53696 * Handle the UITouchPhaseRegion enum values. https://github.com/flutter/flutter/issues/53695 No change in functionality. Only makes the iOS engine build on the latest versions of Xcode and iOS SDK. The enum values cannot be used with the API_AVAILABLE macro because the buildbots have not been updated yet. --- .../ios/framework/Source/FlutterViewController.mm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 889279d0e9ee6..7f8ba6b1830d1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -714,6 +714,10 @@ - (void)goToApplicationLifecycle:(nonnull NSString*)state { return flutter::PointerData::Change::kUp; case UITouchPhaseCancelled: return flutter::PointerData::Change::kCancel; + default: + // TODO(53695): Handle the `UITouchPhaseRegion`... enum values. + FML_DLOG(INFO) << "Unhandled touch phase: " << phase; + break; } return flutter::PointerData::Change::kCancel; @@ -727,9 +731,11 @@ - (void)goToApplicationLifecycle:(nonnull NSString*)state { return flutter::PointerData::DeviceKind::kTouch; case UITouchTypeStylus: return flutter::PointerData::DeviceKind::kStylus; + default: + // TODO(53696): Handle the UITouchTypeIndirectPointer enum value. + FML_DLOG(INFO) << "Unhandled touch type: " << touch.type; + break; } - } else { - return flutter::PointerData::DeviceKind::kTouch; } return flutter::PointerData::DeviceKind::kTouch; From f003d9b41ad8d69d754809b7216f8f5e9822b829 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 31 Mar 2020 15:16:11 -0700 Subject: [PATCH 200/386] GPU to raster rename, patch 2 (#17428) --- fml/raster_thread_merger.cc | 2 +- fml/raster_thread_merger.h | 2 +- lib/ui/compositing/scene_host.cc | 4 +- lib/ui/painting/picture.cc | 2 +- shell/common/shell.cc | 75 ++++++++++++++++---------------- 5 files changed, 43 insertions(+), 42 deletions(-) diff --git a/fml/raster_thread_merger.cc b/fml/raster_thread_merger.cc index e81eebb84d3db..718b41ce327d3 100644 --- a/fml/raster_thread_merger.cc +++ b/fml/raster_thread_merger.cc @@ -63,7 +63,7 @@ RasterThreadStatus RasterThreadMerger::DecrementLease() { lease_term_--; if (lease_term_ == 0) { bool success = task_queues_->Unmerge(platform_queue_id_); - FML_CHECK(success) << "Unable to un-merge the GPU and platform threads."; + FML_CHECK(success) << "Unable to un-merge the raster and platform threads."; is_merged_ = false; return RasterThreadStatus::kUnmergedNow; } diff --git a/fml/raster_thread_merger.h b/fml/raster_thread_merger.h index 847f7e3280aca..1e12e3c41d9b8 100644 --- a/fml/raster_thread_merger.h +++ b/fml/raster_thread_merger.h @@ -33,7 +33,7 @@ class RasterThreadMerger void ExtendLeaseTo(size_t lease_term); // Returns |RasterThreadStatus::kUnmergedNow| if this call resulted in - // splitting the GPU and platform threads. Reduces the lease term by 1. + // splitting the raster and platform threads. Reduces the lease term by 1. RasterThreadStatus DecrementLease(); bool IsMerged() const; diff --git a/lib/ui/compositing/scene_host.cc b/lib/ui/compositing/scene_host.cc index 83d0f561c1e2c..8eb0ff53c6bb1 100644 --- a/lib/ui/compositing/scene_host.cc +++ b/lib/ui/compositing/scene_host.cc @@ -188,8 +188,8 @@ SceneHost::SceneHost(fml::RefPtr viewHolderToken, scene_host_bindings.emplace(std::make_pair(key, scene_host)); }; - // Pass the raw handle to the GPU thead; destroying a |zircon::dart::Handle| - // on that thread can cause a race condition. + // Pass the raw handle to the raster thread; destroying a + // |zircon::dart::Handle| on that thread can cause a race condition. raster_task_runner_->PostTask( [id = koid_, ui_task_runner = diff --git a/lib/ui/painting/picture.cc b/lib/ui/painting/picture.cc index e20f687d73754..511e369d6afdc 100644 --- a/lib/ui/painting/picture.cc +++ b/lib/ui/painting/picture.cc @@ -115,7 +115,7 @@ Dart_Handle Picture::RasterizeToImage(sk_sp picture, delete image_callback; }); - // Kick things off on the GPU. + // Kick things off on the raster rask runner. fml::TaskRunner::RunNowOrPostTask( raster_task_runner, [ui_task_runner, snapshot_delegate, picture, picture_bounds, ui_task] { diff --git a/shell/common/shell.cc b/shell/common/shell.cc index e660c54ede6c3..9125f8881b272 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -605,7 +605,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { // setup/suspension of all activities that may be interacting with the GPU in // a synchronous fashion. fml::AutoResetWaitableEvent latch; - auto gpu_task = + auto raster_task = fml::MakeCopyable([& waiting_for_first_frame = waiting_for_first_frame_, rasterizer = rasterizer_->GetWeakPtr(), // surface = std::move(surface), // @@ -623,19 +623,20 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { // The normal flow executed by this method is that the platform thread is // starting the sequence and waiting on the latch. Later the UI thread posts - // gpu_task to the raster thread which signals the latch. If the GPU the and - // platform threads are the same this results in a deadlock as the gpu_task - // will never be posted to the plaform/raster thread that is blocked on a - // latch. To avoid the described deadlock, if the raster and the platform - // threads are the same, should_post_gpu_task will be false, and then instead - // of posting a task to the raster thread, the ui thread just signals the - // latch and the platform/raster thread follows with executing gpu_task. - bool should_post_gpu_task = task_runners_.GetRasterTaskRunner() != - task_runners_.GetPlatformTaskRunner(); + // raster_task to the raster thread which signals the latch. If the raster and + // the platform threads are the same this results in a deadlock as the + // raster_task will never be posted to the plaform/raster thread that is + // blocked on a latch. To avoid the described deadlock, if the raster and the + // platform threads are the same, should_post_raster_task will be false, and + // then instead of posting a task to the raster thread, the ui thread just + // signals the latch and the platform/raster thread follows with executing + // raster_task. + bool should_post_raster_task = task_runners_.GetRasterTaskRunner() != + task_runners_.GetPlatformTaskRunner(); auto ui_task = [engine = engine_->GetWeakPtr(), // raster_task_runner = task_runners_.GetRasterTaskRunner(), // - gpu_task, should_post_gpu_task, + raster_task, should_post_raster_task, &latch // ] { if (engine) { @@ -643,10 +644,10 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { } // Step 2: Next, tell the raster thread that it should create a surface for // its rasterizer. - if (should_post_gpu_task) { - fml::TaskRunner::RunNowOrPostTask(raster_task_runner, gpu_task); + if (should_post_raster_task) { + fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } else { - // See comment on should_post_gpu_task, in this case we just unblock + // See comment on should_post_raster_task, in this case we just unblock // the platform thread. latch.Signal(); } @@ -674,11 +675,11 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { fml::TaskRunner::RunNowOrPostTask(task_runners_.GetIOTaskRunner(), io_task); latch.Wait(); - if (!should_post_gpu_task) { - // See comment on should_post_gpu_task, in this case the gpu_task + if (!should_post_raster_task) { + // See comment on should_post_raster_task, in this case the raster_task // wasn't executed, and we just run it here as the platform thread // is the raster thread. - gpu_task(); + raster_task(); } } @@ -706,9 +707,9 @@ void Shell::OnPlatformViewDestroyed() { latch.Signal(); }; - auto gpu_task = [rasterizer = rasterizer_->GetWeakPtr(), - io_task_runner = task_runners_.GetIOTaskRunner(), - io_task]() { + auto raster_task = [rasterizer = rasterizer_->GetWeakPtr(), + io_task_runner = task_runners_.GetIOTaskRunner(), + io_task]() { if (rasterizer) { rasterizer->Teardown(); } @@ -718,29 +719,29 @@ void Shell::OnPlatformViewDestroyed() { // The normal flow executed by this method is that the platform thread is // starting the sequence and waiting on the latch. Later the UI thread posts - // gpu_task to the raster thread triggers signaling the latch(on the IO - // thread). If the GPU the and platform threads are the same this results in a - // deadlock as the gpu_task will never be posted to the plaform/raster thread - // that is blocked on a latch. To avoid the described deadlock, if the raster - // and the platform threads are the same, should_post_gpu_task will be false, - // and then instead of posting a task to the raster thread, the ui thread just - // signals the latch and the platform/raster thread follows with executing - // gpu_task. - bool should_post_gpu_task = task_runners_.GetRasterTaskRunner() != - task_runners_.GetPlatformTaskRunner(); + // raster_task to the raster thread triggers signaling the latch(on the IO + // thread). If the raster and the platform threads are the same this results + // in a deadlock as the raster_task will never be posted to the plaform/raster + // thread that is blocked on a latch. To avoid the described deadlock, if the + // raster and the platform threads are the same, should_post_raster_task will + // be false, and then instead of posting a task to the raster thread, the ui + // thread just signals the latch and the platform/raster thread follows with + // executing raster_task. + bool should_post_raster_task = task_runners_.GetRasterTaskRunner() != + task_runners_.GetPlatformTaskRunner(); auto ui_task = [engine = engine_->GetWeakPtr(), raster_task_runner = task_runners_.GetRasterTaskRunner(), - gpu_task, should_post_gpu_task, &latch]() { + raster_task, should_post_raster_task, &latch]() { if (engine) { engine->OnOutputSurfaceDestroyed(); } // Step 1: Next, tell the raster thread that its rasterizer should suspend // access to the underlying surface. - if (should_post_gpu_task) { - fml::TaskRunner::RunNowOrPostTask(raster_task_runner, gpu_task); + if (should_post_raster_task) { + fml::TaskRunner::RunNowOrPostTask(raster_task_runner, raster_task); } else { - // See comment on should_post_gpu_task, in this case we just unblock + // See comment on should_post_raster_task, in this case we just unblock // the platform thread. latch.Signal(); } @@ -750,11 +751,11 @@ void Shell::OnPlatformViewDestroyed() { // surface is about to go away. fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(), ui_task); latch.Wait(); - if (!should_post_gpu_task) { - // See comment on should_post_gpu_task, in this case the gpu_task + if (!should_post_raster_task) { + // See comment on should_post_raster_task, in this case the raster_task // wasn't executed, and we just run it here as the platform thread // is the raster thread. - gpu_task(); + raster_task(); latch.Wait(); } } From 6befd4a804a96cef268f5bc9d37da6610ad2e468 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Wed, 1 Apr 2020 11:25:44 +1300 Subject: [PATCH 201/386] Update FlDartProject to new path format (#17302) Matches the structure used on Windows of assuming a directory format. --- shell/platform/linux/BUILD.gn | 2 + shell/platform/linux/fl_dart_project.cc | 96 +++++++++++++------ shell/platform/linux/fl_view.cc | 9 +- .../public/flutter_linux/fl_dart_project.h | 9 +- 4 files changed, 79 insertions(+), 37 deletions(-) diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index d0221b1e392ca..1b09d54aed6af 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -58,6 +58,8 @@ if (build_linux_shell) { } source_set("flutter_linux") { + public = _public_headers + sources = [ "fl_dart_project.cc", "fl_view.cc", diff --git a/shell/platform/linux/fl_dart_project.cc b/shell/platform/linux/fl_dart_project.cc index cbabc8cb9648f..0a20dd6ade897 100644 --- a/shell/platform/linux/fl_dart_project.cc +++ b/shell/platform/linux/fl_dart_project.cc @@ -16,14 +16,29 @@ struct _FlDartProject { GObject parent_instance; - gchar* assets_path; - gchar* icu_data_path; + gchar* path; }; -enum { PROP_ASSETS_PATH = 1, PROP_ICU_DATA_PATH, PROP_LAST }; +enum { PROP_ASSETS_PATH = 1, PROP_ICU_DATA_PATH, PROP_PATH, PROP_LAST }; G_DEFINE_TYPE(FlDartProject, fl_dart_project, G_TYPE_OBJECT) +static void fl_dart_project_set_path(FlDartProject* self, const gchar* path) { + g_free(self->path); + + if (g_path_is_absolute(path)) + self->path = g_strdup(path); + else { + g_autoptr(GError) error = NULL; + g_autofree gchar* exe_path = g_file_read_link("/proc/self/exe", &error); + if (exe_path == NULL) + g_critical("Failed to determine location of executable: %s", + error->message); + g_autofree gchar* dir = g_path_get_dirname(exe_path); + self->path = g_build_filename(dir, path, NULL); + } +} + static void fl_dart_project_set_property(GObject* object, guint prop_id, const GValue* value, @@ -31,13 +46,8 @@ static void fl_dart_project_set_property(GObject* object, FlDartProject* self = FL_DART_PROJECT(object); switch (prop_id) { - case PROP_ASSETS_PATH: - g_free(self->assets_path); - self->assets_path = g_strdup(g_value_get_string(value)); - break; - case PROP_ICU_DATA_PATH: - g_free(self->icu_data_path); - self->icu_data_path = g_strdup(g_value_get_string(value)); + case PROP_PATH: + fl_dart_project_set_path(self, g_value_get_string(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -53,10 +63,13 @@ static void fl_dart_project_get_property(GObject* object, switch (prop_id) { case PROP_ASSETS_PATH: - g_value_set_string(value, self->assets_path); + g_value_take_string(value, fl_dart_project_get_assets_path(self)); break; case PROP_ICU_DATA_PATH: - g_value_set_string(value, self->icu_data_path); + g_value_take_string(value, fl_dart_project_get_icu_data_path(self)); + break; + case PROP_PATH: + g_value_set_string(value, self->path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -67,8 +80,7 @@ static void fl_dart_project_get_property(GObject* object, static void fl_dart_project_dispose(GObject* object) { FlDartProject* self = FL_DART_PROJECT(object); - g_clear_pointer(&self->assets_path, g_free); - g_clear_pointer(&self->icu_data_path, g_free); + g_clear_pointer(&self->path, g_free); G_OBJECT_CLASS(fl_dart_project_parent_class)->dispose(object); } @@ -82,12 +94,18 @@ static void fl_dart_project_class_init(FlDartProjectClass* klass) { G_OBJECT_CLASS(klass), PROP_ASSETS_PATH, g_param_spec_string( "assets-path", "assets-path", "Path to Flutter assets", nullptr, - static_cast(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS))); + static_cast(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property( G_OBJECT_CLASS(klass), PROP_ICU_DATA_PATH, g_param_spec_string( "icu-data-path", "icu-data-path", "Path to ICU data", nullptr, + static_cast(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); + + g_object_class_install_property( + G_OBJECT_CLASS(klass), PROP_PATH, + g_param_spec_string( + "path", "path", "Path to Flutter project", nullptr, static_cast(G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS))); } @@ -96,18 +114,34 @@ static void fl_dart_project_init(FlDartProject* self) {} /** * fl_dart_project_new: - * @assets_path: a file path, e.g. "build/assets" - * @icu_data_path: a file path, e.g. "build/icudtl.dat" + * @path: a file path, e.g. "my_dart_project" + * + * Create a Flutter project. The project path should contain the following + * top-level items: + * - icudtl.dat (provided as a resource by the Flutter tool) + * - flutter_assets (as built by the Flutter tool) * - * Create a Flutter project. + * The path can either be absolute, or relative to the directory containing the + * running executable. * * Returns: a new #FlDartProject */ -G_MODULE_EXPORT FlDartProject* fl_dart_project_new(const gchar* assets_path, - const gchar* icu_data_path) { +G_MODULE_EXPORT FlDartProject* fl_dart_project_new(const gchar* path) { return static_cast( - g_object_new(fl_dart_project_get_type(), "assets-path", assets_path, - "icu-data-path", icu_data_path, nullptr)); + g_object_new(fl_dart_project_get_type(), "path", path, nullptr)); +} + +/** + * fl_dart_project_get_path: + * @view: a #FlDartProject + * + * Get the path to the directory containing the Flutter application. + * + * Returns: (type filename): a file path, e.g. "/projects/my_dart_project" + */ +G_MODULE_EXPORT const gchar* fl_dart_project_get_path(FlDartProject* self) { + g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); + return self->path; } /** @@ -117,12 +151,12 @@ G_MODULE_EXPORT FlDartProject* fl_dart_project_new(const gchar* assets_path, * Get the path to the directory containing the assets used in the Flutter * application. * - * Returns: a file path, e.g. "build/assets" + * Returns: (type filename): a file path, e.g. + * "/projects/my_dart_project/assets" */ -G_MODULE_EXPORT const gchar* fl_dart_project_get_assets_path( - FlDartProject* self) { +G_MODULE_EXPORT gchar* fl_dart_project_get_assets_path(FlDartProject* self) { g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); - return self->assets_path; + return g_build_filename(self->path, "flutter_assets", NULL); } /** @@ -131,10 +165,10 @@ G_MODULE_EXPORT const gchar* fl_dart_project_get_assets_path( * * Get the path to the ICU data file in the Flutter application. * - * Returns: a file path, e.g. "build/icudtl.dat" + * Returns: (type filename): a file path, e.g. + * "/projects/my_dart_project/icudtl.dat" */ -G_MODULE_EXPORT const gchar* fl_dart_project_get_icu_data_path( - FlDartProject* self) { +G_MODULE_EXPORT gchar* fl_dart_project_get_icu_data_path(FlDartProject* self) { g_return_val_if_fail(FL_IS_DART_PROJECT(self), nullptr); - return self->icu_data_path; + return g_build_filename(self->path, "icudtl.dat", NULL); } diff --git a/shell/platform/linux/fl_view.cc b/shell/platform/linux/fl_view.cc index 218f1aed410c5..879a36201ac17 100644 --- a/shell/platform/linux/fl_view.cc +++ b/shell/platform/linux/fl_view.cc @@ -136,10 +136,15 @@ static gboolean run_flutter_engine(FlView* self) { config.open_gl.fbo_callback = fl_view_gl_fbo_callback; config.open_gl.present = fl_view_gl_present; + g_autofree gchar* assets_path = + fl_dart_project_get_assets_path(self->flutter_project); + g_autofree gchar* icu_data_path = + fl_dart_project_get_icu_data_path(self->flutter_project); + FlutterProjectArgs args = {}; args.struct_size = sizeof(FlutterProjectArgs); - args.assets_path = fl_dart_project_get_assets_path(self->flutter_project); - args.icu_data_path = fl_dart_project_get_icu_data_path(self->flutter_project); + args.assets_path = assets_path; + args.icu_data_path = icu_data_path; FlutterEngineResult result = FlutterEngineInitialize( FLUTTER_ENGINE_VERSION, &config, &args, self, &self->flutter_engine); diff --git a/shell/platform/linux/public/flutter_linux/fl_dart_project.h b/shell/platform/linux/public/flutter_linux/fl_dart_project.h index d2a62b56b72f9..8ee42502179eb 100644 --- a/shell/platform/linux/public/flutter_linux/fl_dart_project.h +++ b/shell/platform/linux/public/flutter_linux/fl_dart_project.h @@ -15,12 +15,13 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE(FlDartProject, fl_dart_project, FL, DART_PROJECT, GObject) -FlDartProject* fl_dart_project_new(const gchar* assets_path, - const gchar* icu_data_path); +FlDartProject* fl_dart_project_new(const gchar* path); -const gchar* fl_dart_project_get_assets_path(FlDartProject* project); +const gchar* fl_dart_project_get_path(FlDartProject* project); -const gchar* fl_dart_project_get_icu_data_path(FlDartProject* project); +gchar* fl_dart_project_get_assets_path(FlDartProject* project); + +gchar* fl_dart_project_get_icu_data_path(FlDartProject* project); G_END_DECLS From b3c7741d1beda73e6811cb61580081af336be3eb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 21:01:03 -0400 Subject: [PATCH 202/386] Roll src/third_party/dart e8c4aed700f5..021fa0fbd410 (17 commits) (#17434) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index a4bc24b9c9f0a..92a5e08679c21 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'e8c4aed700f5effdab0ac76497974e2289b589c9', + 'dart_revision': '021fa0fbd410bd9e211551a1eac4fff516b1b833', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -76,7 +76,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '5802dd3407cae29372060934249432077aff1ec6', + 'dart_pub_rev': '978cc9c4be7d8978348e56d4ae82be8257a59579', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index d27e96e72496a..670dae6890a4f 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 7add498ea3984173a26da831c3ee3445 +Signature: 9aaae39f07cf5a82b253818c715c2a75 UNUSED LICENSES: @@ -7777,6 +7777,7 @@ FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type.cc FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/native_type.h FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/recognized_method.cc FILE: ../../../third_party/dart/runtime/vm/compiler/ffi/recognized_method.h +FILE: ../../../third_party/dart/runtime/vm/compiler/stub_code_compiler.cc FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination.cc FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination.h FILE: ../../../third_party/dart/runtime/vm/compiler/write_barrier_elimination_test.cc From 5cec47786f1db208fef4d8d54ae0fda368e23578 Mon Sep 17 00:00:00 2001 From: mikejurka Date: Tue, 31 Mar 2020 18:07:12 -0700 Subject: [PATCH 203/386] [fuchsia] Re-enable shadows. (#17380) Now that client apps use the same elevations on Fuchsia and other platforms, shadows should look identical. --- flow/layers/physical_shape_layer.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/flow/layers/physical_shape_layer.cc b/flow/layers/physical_shape_layer.cc index 15bad4f5e1f6b..675ec5917c5df 100644 --- a/flow/layers/physical_shape_layer.cc +++ b/flow/layers/physical_shape_layer.cc @@ -140,18 +140,7 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "PhysicalShapeLayer::Paint"); FML_DCHECK(needs_painting()); -#if defined(OS_FUCHSIA) - // TODO(mikejurka,dworsham,liyl): Re-enable shadow drawing here. - // Shadows are not rendered for PhysicalShapeLayers that exist as separate - // system services; this is to maintain compatibility with the previous - // implementation and has the added benefit of requiring smaller textures, - // since extra space is not needed for the shadows. This behavior might change - // after clients adjust their usage of PhysicalShaperLayer to make elevation - // correlate to desired shadow size. - if (false && !child_layer_exists_below_ && elevation_ != 0) { -#else if (elevation_ != 0) { -#endif DrawShadow(context.leaf_nodes_canvas, path_, shadow_color_, elevation_, SkColorGetA(color_) != 0xff, context.frame_device_pixel_ratio); } From 8600d9993993b33477faf2fc4cfb95d855b590f8 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 31 Mar 2020 21:21:01 -0400 Subject: [PATCH 204/386] Roll src/third_party/skia 65d3ad953957..7a3358c80b6e (20 commits) (#17435) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 92a5e08679c21..cbaf3ac343815 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '65d3ad95395791de882d4c8e4804803903534dce', + 'skia_revision': '7a3358c80b6e458c7e60db934c3bc7c697189688', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index d20fdea37c559..01a348119ff49 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 4a73f5eb2f083e66b227356170f0c90f +Signature: 7294b54f0d93db2bcfadfb6dee6d8b2e UNUSED LICENSES: @@ -3882,6 +3882,7 @@ FILE: ../../../third_party/skia/gm/crbug_1041204.cpp FILE: ../../../third_party/skia/gm/crbug_224618.cpp FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DBackendContext.h FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DTypes.h +FILE: ../../../third_party/skia/include/gpu/d3d/GrD3DTypesMinimal.h FILE: ../../../third_party/skia/include/private/GrD3DTypesPriv.h FILE: ../../../third_party/skia/include/private/SkIDChangeListener.h FILE: ../../../third_party/skia/modules/canvaskit/viewer_bindings.cpp From ee274fc61cf46f0b5ea8feff37cf1163244894ad Mon Sep 17 00:00:00 2001 From: mikejurka Date: Tue, 31 Mar 2020 21:49:44 -0700 Subject: [PATCH 205/386] [fuchsia] Fix bug when applying scale. (#17436) We were always scaling z by 0, instead of 1. This caused the z elevation of some layers to be 0, which was incorrect. --- flow/scene_update_context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 407751406bfd6..1dadfd7327f23 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -257,7 +257,7 @@ SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, entity_node().SetScale(decomposition.scale().x(), // decomposition.scale().y(), // - 0.f // + 1.f // ); context.top_scale_x_ *= decomposition.scale().x(); context.top_scale_y_ *= decomposition.scale().y(); From 0911a08048c8c0e7d9eb8e8c82dccbed8b495803 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 31 Mar 2020 22:01:32 -0700 Subject: [PATCH 206/386] If the application says there is a new texture but does not provide one, reuse the last texture. (#17437) This matches the behavior of the OpenGL backend. --- .../darwin/ios/ios_external_texture_metal.h | 2 +- .../darwin/ios/ios_external_texture_metal.mm | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/shell/platform/darwin/ios/ios_external_texture_metal.h b/shell/platform/darwin/ios/ios_external_texture_metal.h index 82e4d6931d426..efedc9f19f4ef 100644 --- a/shell/platform/darwin/ios/ios_external_texture_metal.h +++ b/shell/platform/darwin/ios/ios_external_texture_metal.h @@ -48,7 +48,7 @@ class IOSExternalTextureMetal final : public Texture { // |Texture| void OnTextureUnregistered() override; - sk_sp WrapExternalPixelBuffer(GrContext* context); + sk_sp WrapExternalPixelBuffer(GrContext* context) const; FML_DISALLOW_COPY_AND_ASSIGN(IOSExternalTextureMetal); }; diff --git a/shell/platform/darwin/ios/ios_external_texture_metal.mm b/shell/platform/darwin/ios/ios_external_texture_metal.mm index 7ab55c94b131d..e9871327123b0 100644 --- a/shell/platform/darwin/ios/ios_external_texture_metal.mm +++ b/shell/platform/darwin/ios/ios_external_texture_metal.mm @@ -27,13 +27,15 @@ const SkRect& bounds, bool freeze, GrContext* context) { - if (!freeze && texture_frame_available_) { - external_image_ = nullptr; - } - - if (!external_image_) { - external_image_ = WrapExternalPixelBuffer(context); - texture_frame_available_ = false; + const bool needs_updated_texture = (!freeze && texture_frame_available_) || !external_image_; + + if (needs_updated_texture) { + // If the application told us there was a texture frame available but did not provide one when + // asked for it, reuse the previous texture but make sure to ask again the next time around. + if (auto wrapped_texture = WrapExternalPixelBuffer(context)) { + external_image_ = wrapped_texture; + texture_frame_available_ = false; + } } if (external_image_) { @@ -46,7 +48,7 @@ } } -sk_sp IOSExternalTextureMetal::WrapExternalPixelBuffer(GrContext* context) { +sk_sp IOSExternalTextureMetal::WrapExternalPixelBuffer(GrContext* context) const { auto pixel_buffer = fml::CFRef([external_texture_ copyPixelBuffer]); if (!pixel_buffer) { return nullptr; From 03c4c5b53cbfcb641dab92b99e1fab9773c9756f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 02:16:06 -0400 Subject: [PATCH 207/386] Roll src/third_party/dart 021fa0fbd410..0f89df0c00e7 (4 commits) (#17438) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index cbaf3ac343815..7c1d1e228cbb2 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '021fa0fbd410bd9e211551a1eac4fff516b1b833', + 'dart_revision': '0f89df0c00e7b09287f369920605785e55179a98', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 670dae6890a4f..4fb8ec753e183 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 9aaae39f07cf5a82b253818c715c2a75 +Signature: 2e30af9ab4d1fe33bdb65dccaa867377 UNUSED LICENSES: From f7c6c6d92a126e5ad2a45bd28c6aa1ee1195a281 Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Tue, 31 Mar 2020 23:37:04 -0700 Subject: [PATCH 208/386] Use Metal on iOS by default. (#17431) Fixes https://github.com/flutter/flutter/issues/18208. All Metal for iOS related work items described in https://github.com/orgs/flutter/projects/5 have been completed. --- .../darwin/ios/rendering_api_selection.mm | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/shell/platform/darwin/ios/rendering_api_selection.mm b/shell/platform/darwin/ios/rendering_api_selection.mm index 91b483f1deb3a..fd3ef0c297366 100644 --- a/shell/platform/darwin/ios/rendering_api_selection.mm +++ b/shell/platform/darwin/ios/rendering_api_selection.mm @@ -15,27 +15,9 @@ namespace flutter { -bool ShouldUseSoftwareRenderer() { - return [[[NSProcessInfo processInfo] arguments] containsObject:@"--force-software"]; -} - #if FLUTTER_SHELL_ENABLE_METAL bool ShouldUseMetalRenderer() { - // If there is a command line argument that says Metal should not be used, that takes precedence - // over everything else. This allows disabling Metal on a per run basis to check for regressions - // on an application that has otherwise opted into Metal on an iOS version that supports it. - if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--disable-metal"]) { - return false; - } - - // If the application wants to use metal on a per run basis with disregard for version checks or - // plist based opt ins, respect that opinion. This allows selectively testing features on older - // version of iOS than those explicitly stated as being supported. - if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--force-metal"]) { - return true; - } - - // Flutter supports Metal on all devices with Apple A7 SoC or above that have been update to or + // Flutter supports Metal on all devices with Apple A7 SoC or above that have been updated to or // past iOS 10.0. The processor was selected as it is the first version at which Metal was // supported. The iOS version floor was selected due to the availability of features used by Skia. bool ios_version_supports_metal = false; @@ -43,12 +25,7 @@ bool ShouldUseMetalRenderer() { auto device = MTLCreateSystemDefaultDevice(); ios_version_supports_metal = [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]; } - - // The application must opt-in by default to use Metal without command line flags. - bool application_opts_into_metal = - [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"io.flutter.metal_preview"] boolValue]; - - return ios_version_supports_metal && application_opts_into_metal; + return ios_version_supports_metal; } #endif // FLUTTER_SHELL_ENABLE_METAL @@ -58,10 +35,6 @@ IOSRenderingAPI GetRenderingAPIForProcess() { #endif // TARGET_IPHONE_SIMULATOR #if FLUTTER_SHELL_ENABLE_METAL - static bool should_use_software = ShouldUseSoftwareRenderer(); - if (should_use_software) { - return IOSRenderingAPI::kSoftware; - } static bool should_use_metal = ShouldUseMetalRenderer(); if (should_use_metal) { return IOSRenderingAPI::kMetal; From bcc144d0d138cf1f2abe198c1108367c114e8da4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 04:31:02 -0400 Subject: [PATCH 209/386] Roll fuchsia/sdk/core/mac-amd64 from lPmiR... to iP6DE... (#17442) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 7c1d1e228cbb2..ffa16cf4d4d20 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'lPmiRXjvD_DKq35O_7qZIgkNQLduwHGp6UbVp6HV3bAC' + 'version': 'iP6DEtpFZFlZM-2hjt5NwAo-ZVhSw0arGA-hX7dRAboC' } ], 'condition': 'host_os == "mac"', From 767b48abc91e474ed170b573ee1262ee1088bb0e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 06:56:01 -0400 Subject: [PATCH 210/386] Roll src/third_party/dart 0f89df0c00e7..eea97179386a (3 commits) (#17443) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index ffa16cf4d4d20..6729026dfd684 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '0f89df0c00e7b09287f369920605785e55179a98', + 'dart_revision': 'eea97179386a9ced7d68452cea158345f4019baa', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 2b37d1d46ce56b9dbecd52283156223b09a83437 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 09:56:01 -0400 Subject: [PATCH 211/386] Roll src/third_party/skia 7a3358c80b6e..0f5e7581a004 (1 commits) (#17444) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6729026dfd684..f927e4c5e9849 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '7a3358c80b6e458c7e60db934c3bc7c697189688', + 'skia_revision': '0f5e7581a0040082ccf746f5ba2757f728d17761', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 01a348119ff49..7ffc5e54ef0b6 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 7294b54f0d93db2bcfadfb6dee6d8b2e +Signature: b60869e00952843009d68e7cc00b2686 UNUSED LICENSES: From c9506cb8e93e5e8879152ff5c948b175abb5b997 Mon Sep 17 00:00:00 2001 From: Brian Osman Date: Wed, 1 Apr 2020 10:27:50 -0400 Subject: [PATCH 212/386] Finish removing deprecated SkCanvas virtuals (#17445) API staging is complete - these defines have been removed. --- shell/common/canvas_spy.cc | 17 ----------------- shell/common/canvas_spy.h | 22 ---------------------- testing/mock_canvas.cc | 19 ------------------- testing/mock_canvas.h | 20 -------------------- 4 files changed, 78 deletions(-) diff --git a/shell/common/canvas_spy.cc b/shell/common/canvas_spy.cc index 2cdb71f7eadaf..5b62a34953a23 100644 --- a/shell/common/canvas_spy.cc +++ b/shell/common/canvas_spy.cc @@ -124,23 +124,6 @@ void DidDrawCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { MarkDrawIfNonTransparentPaint(paint); } -#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) -void DidDrawCanvas::onDrawBitmap(const SkBitmap& bitmap, - SkScalar x, - SkScalar y, - const SkPaint* paint) { - did_draw_ = true; -} - -void DidDrawCanvas::onDrawBitmapRect(const SkBitmap& bitmap, - const SkRect* src, - const SkRect& dst, - const SkPaint* paint, - SrcRectConstraint constraint) { - did_draw_ = true; -} -#endif - void DidDrawCanvas::onDrawImage(const SkImage* image, SkScalar left, SkScalar top, diff --git a/shell/common/canvas_spy.h b/shell/common/canvas_spy.h index 4aab238eecf48..d5c75be34df40 100644 --- a/shell/common/canvas_spy.h +++ b/shell/common/canvas_spy.h @@ -127,21 +127,6 @@ class DidDrawCanvas final : public SkCanvasVirtualEnforcer { // |SkCanvasVirtualEnforcer| void onDrawPath(const SkPath&, const SkPaint&) override; -#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) - // |SkCanvasVirtualEnforcer| - void onDrawBitmap(const SkBitmap&, - SkScalar left, - SkScalar top, - const SkPaint*) override; - - // |SkCanvasVirtualEnforcer| - void onDrawBitmapRect(const SkBitmap&, - const SkRect* src, - const SkRect& dst, - const SkPaint*, - SrcRectConstraint) override; -#endif - // |SkCanvasVirtualEnforcer| void onDrawImage(const SkImage*, SkScalar left, @@ -168,13 +153,6 @@ class DidDrawCanvas final : public SkCanvasVirtualEnforcer { const SkPaint*) override; // |SkCanvasVirtualEnforcer| -#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL - void onDrawVerticesObject(const SkVertices*, - const SkVertices::Bone[], - int, - SkBlendMode, - const SkPaint&) override {} -#endif void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc index 88ab5f9ed196d..300f2d4288c0d 100644 --- a/testing/mock_canvas.cc +++ b/testing/mock_canvas.cc @@ -225,15 +225,6 @@ void MockCanvas::onDrawRRect(const SkRRect&, const SkPaint&) { FML_DCHECK(false); } -#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) -void MockCanvas::onDrawBitmap(const SkBitmap&, - SkScalar, - SkScalar, - const SkPaint*) { - FML_DCHECK(false); -} -#endif - void MockCanvas::onDrawImage(const SkImage*, SkScalar, SkScalar, @@ -241,16 +232,6 @@ void MockCanvas::onDrawImage(const SkImage*, FML_DCHECK(false); } -#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) -void MockCanvas::onDrawBitmapRect(const SkBitmap&, - const SkRect*, - const SkRect&, - const SkPaint*, - SrcRectConstraint) { - FML_DCHECK(false); -} -#endif - void MockCanvas::onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h index e7438db5fba86..2957783dbe2b5 100644 --- a/testing/mock_canvas.h +++ b/testing/mock_canvas.h @@ -195,13 +195,6 @@ class MockCanvas : public SkCanvasVirtualEnforcer { bool, const SkPaint&) override; void onDrawRRect(const SkRRect&, const SkPaint&) override; -#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) - void onDrawBitmapRect(const SkBitmap&, - const SkRect*, - const SkRect&, - const SkPaint*, - SrcRectConstraint) override; -#endif void onDrawImage(const SkImage* image, SkScalar x, SkScalar y, @@ -215,23 +208,10 @@ class MockCanvas : public SkCanvasVirtualEnforcer { const SkIRect&, const SkRect&, const SkPaint*) override; -#if defined(SK_SUPPORT_LEGACY_ONDRAWBITMAP_VIRTUALS) - void onDrawBitmap(const SkBitmap& bitmap, - SkScalar x, - SkScalar y, - const SkPaint* paint) override; -#endif void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override; -#ifdef SK_SUPPORT_LEGACY_DRAWVERTS_VIRTUAL - void onDrawVerticesObject(const SkVertices*, - const SkVertices::Bone[], - int, - SkBlendMode, - const SkPaint&) override {} -#endif void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; From 977d9325f71a4cf0158221e836f5cbbaf3c15938 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 12:21:03 -0400 Subject: [PATCH 213/386] Roll src/third_party/skia 0f5e7581a004..e8d2080a34ed (2 commits) (#17447) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f927e4c5e9849..1ee27bb02b284 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '0f5e7581a0040082ccf746f5ba2757f728d17761', + 'skia_revision': 'e8d2080a34ed3edfee6b979c8b07ad6e96b240d8', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 7ffc5e54ef0b6..2a146073c524d 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: b60869e00952843009d68e7cc00b2686 +Signature: 94449c0766bdad0c6d983e3777d226a5 UNUSED LICENSES: From efbc375ee5fbde1589b655a20e24066d8f233a74 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 12:26:03 -0400 Subject: [PATCH 214/386] Roll src/third_party/dart eea97179386a..b177e44ffa44 (5 commits) (#17448) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1ee27bb02b284..321199c6d0eea 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'eea97179386a9ced7d68452cea158345f4019baa', + 'dart_revision': 'b177e44ffa442d4bc5f55af5c60269a1a600e5ab', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 4fb8ec753e183..8bc6bbdd2210a 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 2e30af9ab4d1fe33bdb65dccaa867377 +Signature: 65458cd4212e207db2cddcccee596174 UNUSED LICENSES: From 46277fd2ca541dc0a1ff0bf2599f89e5ae9d5a88 Mon Sep 17 00:00:00 2001 From: cjng96 Date: Thu, 2 Apr 2020 01:32:18 +0900 Subject: [PATCH 215/386] Remove code which skips keyEvent from input control on web (#17242) --- lib/web_ui/lib/src/engine/keyboard.dart | 34 ------------------------- lib/web_ui/test/keyboard_test.dart | 4 +-- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index 4183cd3054256..f9408d1438b42 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -5,22 +5,6 @@ // @dart = 2.6 part of engine; -/// Contains a whitelist of keys that must be sent to Flutter under all -/// circumstances. -/// -/// When keys are pressed in a text field, we generally don't want to send them -/// to Flutter. This list of keys is the exception to that rule. Keys in this -/// list will be sent to Flutter even if pressed in a text field. -/// -/// A good example is the "Tab" and "Shift" keys which are used by the framework -/// to move focus between text fields. -const List _alwaysSentKeys = [ - 'Alt', - 'Control', - 'Meta', - 'Shift', - 'Tab', -]; /// Provides keyboard bindings, such as the `flutter/keyevent` channel. class Keyboard { @@ -72,10 +56,6 @@ class Keyboard { return; } - if (_shouldIgnoreEvent(event)) { - return; - } - if (_shouldPreventDefault(event)) { event.preventDefault(); } @@ -92,20 +72,6 @@ class Keyboard { _messageCodec.encodeMessage(eventData), _noopCallback); } - /// Whether the [Keyboard] class should ignore the given [html.KeyboardEvent]. - /// - /// When this method returns true, it prevents the keyboard event from being - /// sent to Flutter. - bool _shouldIgnoreEvent(html.KeyboardEvent event) { - // Keys in the [_alwaysSentKeys] list should never be ignored. - if (_alwaysSentKeys.contains(event.key)) { - return false; - } - // Other keys should be ignored if triggered on a text field. - return event.target is html.Element && - HybridTextEditing.isEditingElement(event.target); - } - bool _shouldPreventDefault(html.KeyboardEvent event) { switch (event.key) { case 'Tab': diff --git a/lib/web_ui/test/keyboard_test.dart b/lib/web_ui/test/keyboard_test.dart index 1b9e0eda0bf71..c365f3c69a6b1 100644 --- a/lib/web_ui/test/keyboard_test.dart +++ b/lib/web_ui/test/keyboard_test.dart @@ -242,7 +242,7 @@ void main() { Keyboard.instance.dispose(); }); - test('ignores keyboard events triggered on text fields', () { + test('keyboard events should be triggered on text fields', () { Keyboard.initialize(); int count = 0; @@ -260,7 +260,7 @@ void main() { ); expect(event.defaultPrevented, isFalse); - expect(count, 0); + expect(count, 1); }); Keyboard.instance.dispose(); From 8d723aeb0a428df2ff7950723ca615fdf9acdf1d Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 1 Apr 2020 10:49:24 -0700 Subject: [PATCH 216/386] Roll Clang to v11, roll buildroot to fe13f79dce4ce9aab3209f11988d825dcf580b4a, allow newly exported symbols (#17440) * Buildroot to fe13f79dce4ce9aab3209f11988d825dcf580b4a * whitelist more symbols --- DEPS | 6 +- testing/symbols/verify_exported.dart | 84 +++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 321199c6d0eea..cf9a089e7a7ef 100644 --- a/DEPS +++ b/DEPS @@ -136,7 +136,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'fe13f79dce4ce9aab3209f11988d825dcf580b4a', # Fuchsia compatibility # @@ -517,8 +517,8 @@ deps = { 'src/buildtools/{host_os}-x64/clang': { 'packages': [ { - 'package': 'fuchsia/clang/${{platform}}', - 'version': 'git_revision:de39621f0f03f20633bdfa50bde97a3908bf6e98' + 'package': 'fuchsia/third_party/clang/${{platform}}', + 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' } ], 'condition': 'host_os == "mac" or host_os == "linux"', diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 2b822d08fea79..57598a59e8241 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -110,8 +110,90 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { 'JNI_OnLoad': 'T', '_binary_icudtl_dat_size': 'A', '_binary_icudtl_dat_start': 'D', + // TODO(fxb/47943): Remove these once Clang lld does not expose them. + // arm + '__adddf3': 'T', + '__addsf3': 'T', + '__aeabi_cdcmpeq': 'T', + '__aeabi_cdcmple': 'T', + '__aeabi_cdrcmple': 'T', + '__aeabi_d2lz': 'T', + '__aeabi_d2uiz': 'T', + '__aeabi_d2ulz': 'T', + '__aeabi_dadd': 'T', + '__aeabi_dcmpeq': 'T', + '__aeabi_dcmpge': 'T', + '__aeabi_dcmpgt': 'T', + '__aeabi_dcmple': 'T', + '__aeabi_dcmplt': 'T', + '__aeabi_ddiv': 'T', + '__aeabi_dmul': 'T', + '__aeabi_drsub': 'T', + '__aeabi_dsub': 'T', + '__aeabi_f2d': 'T', + '__aeabi_fadd': 'T', + '__aeabi_frsub': 'T', + '__aeabi_fsub': 'T', + '__aeabi_i2d': 'T', + '__aeabi_i2f': 'T', + '__aeabi_l2d': 'T', + '__aeabi_l2f': 'T', + '__aeabi_lasr': 'T', + '__aeabi_ldivmod': 'T', + '__aeabi_llsl': 'T', + '__aeabi_llsr': 'T', + '__aeabi_ui2d': 'T', + '__aeabi_ui2f': 'T', + '__aeabi_uidiv': 'T', + '__aeabi_uidivmod': 'T', + '__aeabi_ul2d': 'T', + '__aeabi_ul2f': 'T', + '__aeabi_uldivmod': 'T', + '__ashldi3': 'T', + '__ashrdi3': 'T', + '__cmpdf2': 'T', + '__divdf3': 'T', + '__divdi3': 'T', + '__eqdf2': 'T', + '__extendsfdf2': 'T', + '__fixdfdi': 'T', + '__fixunsdfdi': 'T', + '__fixunsdfsi': 'T', + '__floatdidf': 'T', + '__floatdisf': 'T', + '__floatsidf': 'T', + '__floatsisf': 'T', + '__floatundidf': 'T', + '__floatundisf': 'T', + '__floatunsidf': 'T', + '__floatunsisf': 'T', + '__gedf2': 'T', + '__gnu_ldivmod_helper': 'T', + '__gnu_uldivmod_helper': 'T', + '__gtdf2': 'T', + '__ledf2': 'T', + '__lshrdi3': 'T', + '__ltdf2': 'T', + '__muldf3': 'T', + '__nedf2': 'T', + '__subdf3': 'T', + '__subsf3': 'T', + '__udivdi3': 'T', + '__udivsi3': 'T', + // arm64 + '__clz_tab': 'R', + '__udivti3': 'T', + // arm64 && x64 + '__emutls_get_address': 'T', + '__emutls_register_common': 'T', }; - if (!const MapEquality().equals(entryMap, expectedSymbols)) { + final Map badSymbols = {}; + for (final String key in entryMap.keys) { + if (entryMap[key] != expectedSymbols[key]) { + badSymbols[key] = entryMap[key]; + } + } + if (badSymbols.isNotEmpty) { print('ERROR: $libFlutter exports the wrong symbols'); print(' Expected $expectedSymbols'); print(' Library has $entryMap.'); From 2876ad5114e54469ea34382a8a2eaa3bf431f511 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 1 Apr 2020 10:51:03 -0700 Subject: [PATCH 217/386] Remove me from auto-assign list (#17446) --- .github/auto_assign.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index 7bdabfbc1e3a7..638c686bd5cdc 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -20,7 +20,6 @@ reviewers: - franciscojma86 - cbracken - flar - - stuartmorgan # A number of reviewers added to the pull request # Set 0 to add all the reviewers (default: 0) From a870bc533c5bce9526563c3d2f40370e450b85f6 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 1 Apr 2020 11:06:07 -0700 Subject: [PATCH 218/386] Revert "Roll Clang to v11, roll buildroot to fe13f79dce4ce9aab3209f11988d825dcf580b4a, allow newly exported symbols (#17440)" (#17451) This reverts commit 8d723aeb0a428df2ff7950723ca615fdf9acdf1d. --- DEPS | 6 +- testing/symbols/verify_exported.dart | 84 +--------------------------- 2 files changed, 4 insertions(+), 86 deletions(-) diff --git a/DEPS b/DEPS index cf9a089e7a7ef..321199c6d0eea 100644 --- a/DEPS +++ b/DEPS @@ -136,7 +136,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'fe13f79dce4ce9aab3209f11988d825dcf580b4a', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', # Fuchsia compatibility # @@ -517,8 +517,8 @@ deps = { 'src/buildtools/{host_os}-x64/clang': { 'packages': [ { - 'package': 'fuchsia/third_party/clang/${{platform}}', - 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' + 'package': 'fuchsia/clang/${{platform}}', + 'version': 'git_revision:de39621f0f03f20633bdfa50bde97a3908bf6e98' } ], 'condition': 'host_os == "mac" or host_os == "linux"', diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 57598a59e8241..2b822d08fea79 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -110,90 +110,8 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { 'JNI_OnLoad': 'T', '_binary_icudtl_dat_size': 'A', '_binary_icudtl_dat_start': 'D', - // TODO(fxb/47943): Remove these once Clang lld does not expose them. - // arm - '__adddf3': 'T', - '__addsf3': 'T', - '__aeabi_cdcmpeq': 'T', - '__aeabi_cdcmple': 'T', - '__aeabi_cdrcmple': 'T', - '__aeabi_d2lz': 'T', - '__aeabi_d2uiz': 'T', - '__aeabi_d2ulz': 'T', - '__aeabi_dadd': 'T', - '__aeabi_dcmpeq': 'T', - '__aeabi_dcmpge': 'T', - '__aeabi_dcmpgt': 'T', - '__aeabi_dcmple': 'T', - '__aeabi_dcmplt': 'T', - '__aeabi_ddiv': 'T', - '__aeabi_dmul': 'T', - '__aeabi_drsub': 'T', - '__aeabi_dsub': 'T', - '__aeabi_f2d': 'T', - '__aeabi_fadd': 'T', - '__aeabi_frsub': 'T', - '__aeabi_fsub': 'T', - '__aeabi_i2d': 'T', - '__aeabi_i2f': 'T', - '__aeabi_l2d': 'T', - '__aeabi_l2f': 'T', - '__aeabi_lasr': 'T', - '__aeabi_ldivmod': 'T', - '__aeabi_llsl': 'T', - '__aeabi_llsr': 'T', - '__aeabi_ui2d': 'T', - '__aeabi_ui2f': 'T', - '__aeabi_uidiv': 'T', - '__aeabi_uidivmod': 'T', - '__aeabi_ul2d': 'T', - '__aeabi_ul2f': 'T', - '__aeabi_uldivmod': 'T', - '__ashldi3': 'T', - '__ashrdi3': 'T', - '__cmpdf2': 'T', - '__divdf3': 'T', - '__divdi3': 'T', - '__eqdf2': 'T', - '__extendsfdf2': 'T', - '__fixdfdi': 'T', - '__fixunsdfdi': 'T', - '__fixunsdfsi': 'T', - '__floatdidf': 'T', - '__floatdisf': 'T', - '__floatsidf': 'T', - '__floatsisf': 'T', - '__floatundidf': 'T', - '__floatundisf': 'T', - '__floatunsidf': 'T', - '__floatunsisf': 'T', - '__gedf2': 'T', - '__gnu_ldivmod_helper': 'T', - '__gnu_uldivmod_helper': 'T', - '__gtdf2': 'T', - '__ledf2': 'T', - '__lshrdi3': 'T', - '__ltdf2': 'T', - '__muldf3': 'T', - '__nedf2': 'T', - '__subdf3': 'T', - '__subsf3': 'T', - '__udivdi3': 'T', - '__udivsi3': 'T', - // arm64 - '__clz_tab': 'R', - '__udivti3': 'T', - // arm64 && x64 - '__emutls_get_address': 'T', - '__emutls_register_common': 'T', }; - final Map badSymbols = {}; - for (final String key in entryMap.keys) { - if (entryMap[key] != expectedSymbols[key]) { - badSymbols[key] = entryMap[key]; - } - } - if (badSymbols.isNotEmpty) { + if (!const MapEquality().equals(entryMap, expectedSymbols)) { print('ERROR: $libFlutter exports the wrong symbols'); print(' Expected $expectedSymbols'); print(' Library has $entryMap.'); From fd702b2ac7620ac129bc1679bba5572a359e2644 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 15:16:01 -0400 Subject: [PATCH 219/386] Roll src/third_party/skia e8d2080a34ed..bfc09267bdb4 (8 commits) (#17450) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 321199c6d0eea..17961f25230f6 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e8d2080a34ed3edfee6b979c8b07ad6e96b240d8', + 'skia_revision': 'bfc09267bdb446fb31110099f029a846adc001fe', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2a146073c524d..36802d3fc7433 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 94449c0766bdad0c6d983e3777d226a5 +Signature: a8a81675aedb136a08fdd2ffe69e72e9 UNUSED LICENSES: @@ -3891,7 +3891,6 @@ FILE: ../../../third_party/skia/src/core/SkVM_fwd.h FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.cpp FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.h FILE: ../../../third_party/skia/src/gpu/GrManagedResource.cpp -FILE: ../../../third_party/skia/src/gpu/d3d/GrD3D12.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCommandList.cpp From 76fe83c2c44b6d5433b9c0cee3359ebd97b1823a Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Wed, 1 Apr 2020 13:08:01 -0700 Subject: [PATCH 220/386] Started clearing out the semantics information in Dart as well as deleting the AccessibilityBridge. (#17433) --- shell/platform/darwin/ios/platform_view_ios.h | 20 ++++++++- .../platform/darwin/ios/platform_view_ios.mm | 41 ++++++++++++++++--- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/shell/platform/darwin/ios/platform_view_ios.h b/shell/platform/darwin/ios/platform_view_ios.h index 0412acd722e98..95f6fc0ecfacf 100644 --- a/shell/platform/darwin/ios/platform_view_ios.h +++ b/shell/platform/darwin/ios/platform_view_ios.h @@ -66,6 +66,24 @@ class PlatformViewIOS final : public PlatformView { id observer_; }; + /// Smart pointer that guarentees we communicate clearing Accessibility + /// information to Dart. + class AccessibilityBridgePtr { + public: + AccessibilityBridgePtr(const std::function& set_semantics_enabled); + AccessibilityBridgePtr(const std::function& set_semantics_enabled, + AccessibilityBridge* bridge); + ~AccessibilityBridgePtr(); + explicit operator bool() const noexcept { return static_cast(accessibility_bridge_); } + AccessibilityBridge* operator->() const noexcept { return accessibility_bridge_.get(); } + void reset(AccessibilityBridge* bridge = nullptr); + + private: + FML_DISALLOW_COPY_AND_ASSIGN(AccessibilityBridgePtr); + std::unique_ptr accessibility_bridge_; + std::function set_semantics_enabled_; + }; + fml::WeakPtr owner_controller_; // Since the `ios_surface_` is created on the platform thread but // used on the raster thread we need to protect it with a mutex. @@ -73,7 +91,7 @@ class PlatformViewIOS final : public PlatformView { std::unique_ptr ios_surface_; std::shared_ptr ios_context_; PlatformMessageRouter platform_message_router_; - std::unique_ptr accessibility_bridge_; + AccessibilityBridgePtr accessibility_bridge_; fml::scoped_nsprotocol text_input_plugin_; fml::closure firstFrameCallback_; ScopedObserver dealloc_view_controller_observer_; diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index ff5118207cfaf..988ae8b3849c0 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -15,11 +15,41 @@ namespace flutter { +PlatformViewIOS::AccessibilityBridgePtr::AccessibilityBridgePtr( + const std::function& set_semantics_enabled) + : AccessibilityBridgePtr(set_semantics_enabled, nullptr) {} + +PlatformViewIOS::AccessibilityBridgePtr::AccessibilityBridgePtr( + const std::function& set_semantics_enabled, + AccessibilityBridge* bridge) + : accessibility_bridge_(bridge), set_semantics_enabled_(set_semantics_enabled) { + if (bridge) { + set_semantics_enabled_(true); + } +} + +PlatformViewIOS::AccessibilityBridgePtr::~AccessibilityBridgePtr() { + if (accessibility_bridge_) { + set_semantics_enabled_(false); + } +} + +void PlatformViewIOS::AccessibilityBridgePtr::reset(AccessibilityBridge* bridge) { + if (accessibility_bridge_) { + set_semantics_enabled_(false); + } + accessibility_bridge_.reset(bridge); + if (accessibility_bridge_) { + set_semantics_enabled_(true); + } +} + PlatformViewIOS::PlatformViewIOS(PlatformView::Delegate& delegate, IOSRenderingAPI rendering_api, flutter::TaskRunners task_runners) : PlatformView(delegate, std::move(task_runners)), - ios_context_(IOSContext::Create(rendering_api)) {} + ios_context_(IOSContext::Create(rendering_api)), + accessibility_bridge_([this](bool enabled) { PlatformView::SetSemanticsEnabled(enabled); }) {} PlatformViewIOS::~PlatformViewIOS() = default; @@ -117,13 +147,14 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), return; } if (enabled && !accessibility_bridge_) { - accessibility_bridge_ = std::make_unique( - static_cast(owner_controller_.get().view), this, - [owner_controller_.get() platformViewsController]); + accessibility_bridge_.reset( + new AccessibilityBridge(static_cast(owner_controller_.get().view), this, + [owner_controller_.get() platformViewsController])); } else if (!enabled && accessibility_bridge_) { accessibility_bridge_.reset(); + } else { + PlatformView::SetSemanticsEnabled(enabled); } - PlatformView::SetSemanticsEnabled(enabled); } // |shell:PlatformView| From a4026cc9f16a93fbce6c94af3ddaf971d363c195 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 1 Apr 2020 16:32:13 -0700 Subject: [PATCH 221/386] Reland Clang 11, Roll buildroot to 1bc40a5f916adb93d87e2a2230a0ec43db876c1c (#17457) * Reland "Roll Clang to v11, roll buildroot to fe13f79dce4ce9aab3209f11988d825dcf580b4a, allow newly exported symbols (#17440)" (#17451)" This reverts commit a870bc533c5bce9526563c3d2f40370e450b85f6. * skip golden that is different on macos * buildroot to 1bc40a5f916adb93d87e2a2230a0ec43db876c1c --- DEPS | 6 +- .../performance_overlay_layer_unittests.cc | 2 + .../embedder/tests/embedder_unittests.cc | 8 +- testing/symbols/verify_exported.dart | 84 ++++++++++++++++++- 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index 17961f25230f6..9d39c68a0e1f4 100644 --- a/DEPS +++ b/DEPS @@ -136,7 +136,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '1bc40a5f916adb93d87e2a2230a0ec43db876c1c', # Fuchsia compatibility # @@ -517,8 +517,8 @@ deps = { 'src/buildtools/{host_os}-x64/clang': { 'packages': [ { - 'package': 'fuchsia/clang/${{platform}}', - 'version': 'git_revision:de39621f0f03f20633bdfa50bde97a3908bf6e98' + 'package': 'fuchsia/third_party/clang/${{platform}}', + 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' } ], 'condition': 'host_os == "mac" or host_os == "linux"', diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index cf0cd68853e59..3fcb2e04e835b 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -88,6 +88,8 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; + // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all + // platforms. #if !defined(OS_LINUX) GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 291d8cd266163..78286fcf54152 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1430,7 +1430,7 @@ TEST_F(EmbedderTest, }); context.GetCompositor().SetPlatformViewRendererCallback( - [&](const FlutterLayer& layer, GrContext * + [&](const FlutterLayer& layer, GrContext* /* don't use because software compositor */) -> sk_sp { auto surface = CreateRenderSurface( layer, nullptr /* null because software compositor */); @@ -3001,6 +3001,12 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { auto renderered_scene = context.GetNextSceneImage(); latch.Wait(); + + // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all + // platforms. +#if !defined(OS_LINUX) + GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; +#endif // OS_LINUX ASSERT_TRUE(ImageMatchesFixture("verifyb143464703_soft_noxform.png", renderered_scene)); } diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 2b822d08fea79..57598a59e8241 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -110,8 +110,90 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { 'JNI_OnLoad': 'T', '_binary_icudtl_dat_size': 'A', '_binary_icudtl_dat_start': 'D', + // TODO(fxb/47943): Remove these once Clang lld does not expose them. + // arm + '__adddf3': 'T', + '__addsf3': 'T', + '__aeabi_cdcmpeq': 'T', + '__aeabi_cdcmple': 'T', + '__aeabi_cdrcmple': 'T', + '__aeabi_d2lz': 'T', + '__aeabi_d2uiz': 'T', + '__aeabi_d2ulz': 'T', + '__aeabi_dadd': 'T', + '__aeabi_dcmpeq': 'T', + '__aeabi_dcmpge': 'T', + '__aeabi_dcmpgt': 'T', + '__aeabi_dcmple': 'T', + '__aeabi_dcmplt': 'T', + '__aeabi_ddiv': 'T', + '__aeabi_dmul': 'T', + '__aeabi_drsub': 'T', + '__aeabi_dsub': 'T', + '__aeabi_f2d': 'T', + '__aeabi_fadd': 'T', + '__aeabi_frsub': 'T', + '__aeabi_fsub': 'T', + '__aeabi_i2d': 'T', + '__aeabi_i2f': 'T', + '__aeabi_l2d': 'T', + '__aeabi_l2f': 'T', + '__aeabi_lasr': 'T', + '__aeabi_ldivmod': 'T', + '__aeabi_llsl': 'T', + '__aeabi_llsr': 'T', + '__aeabi_ui2d': 'T', + '__aeabi_ui2f': 'T', + '__aeabi_uidiv': 'T', + '__aeabi_uidivmod': 'T', + '__aeabi_ul2d': 'T', + '__aeabi_ul2f': 'T', + '__aeabi_uldivmod': 'T', + '__ashldi3': 'T', + '__ashrdi3': 'T', + '__cmpdf2': 'T', + '__divdf3': 'T', + '__divdi3': 'T', + '__eqdf2': 'T', + '__extendsfdf2': 'T', + '__fixdfdi': 'T', + '__fixunsdfdi': 'T', + '__fixunsdfsi': 'T', + '__floatdidf': 'T', + '__floatdisf': 'T', + '__floatsidf': 'T', + '__floatsisf': 'T', + '__floatundidf': 'T', + '__floatundisf': 'T', + '__floatunsidf': 'T', + '__floatunsisf': 'T', + '__gedf2': 'T', + '__gnu_ldivmod_helper': 'T', + '__gnu_uldivmod_helper': 'T', + '__gtdf2': 'T', + '__ledf2': 'T', + '__lshrdi3': 'T', + '__ltdf2': 'T', + '__muldf3': 'T', + '__nedf2': 'T', + '__subdf3': 'T', + '__subsf3': 'T', + '__udivdi3': 'T', + '__udivsi3': 'T', + // arm64 + '__clz_tab': 'R', + '__udivti3': 'T', + // arm64 && x64 + '__emutls_get_address': 'T', + '__emutls_register_common': 'T', }; - if (!const MapEquality().equals(entryMap, expectedSymbols)) { + final Map badSymbols = {}; + for (final String key in entryMap.keys) { + if (entryMap[key] != expectedSymbols[key]) { + badSymbols[key] = entryMap[key]; + } + } + if (badSymbols.isNotEmpty) { print('ERROR: $libFlutter exports the wrong symbols'); print(' Expected $expectedSymbols'); print(' Library has $entryMap.'); From 9eacd028cdba5c6efef327342dc53fab61474462 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 1 Apr 2020 17:50:57 -0700 Subject: [PATCH 222/386] Revert "Reland Clang 11, Roll buildroot to 1bc40a5f916adb93d87e2a2230a0ec43db876c1c (#17457)" (#17464) This reverts commit a4026cc9f16a93fbce6c94af3ddaf971d363c195. --- DEPS | 6 +- .../performance_overlay_layer_unittests.cc | 2 - .../embedder/tests/embedder_unittests.cc | 8 +- testing/symbols/verify_exported.dart | 84 +------------------ 4 files changed, 5 insertions(+), 95 deletions(-) diff --git a/DEPS b/DEPS index 9d39c68a0e1f4..17961f25230f6 100644 --- a/DEPS +++ b/DEPS @@ -136,7 +136,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '1bc40a5f916adb93d87e2a2230a0ec43db876c1c', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', # Fuchsia compatibility # @@ -517,8 +517,8 @@ deps = { 'src/buildtools/{host_os}-x64/clang': { 'packages': [ { - 'package': 'fuchsia/third_party/clang/${{platform}}', - 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' + 'package': 'fuchsia/clang/${{platform}}', + 'version': 'git_revision:de39621f0f03f20633bdfa50bde97a3908bf6e98' } ], 'condition': 'host_os == "mac" or host_os == "linux"', diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 3fcb2e04e835b..cf0cd68853e59 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -88,8 +88,6 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; - // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all - // platforms. #if !defined(OS_LINUX) GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 78286fcf54152..291d8cd266163 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1430,7 +1430,7 @@ TEST_F(EmbedderTest, }); context.GetCompositor().SetPlatformViewRendererCallback( - [&](const FlutterLayer& layer, GrContext* + [&](const FlutterLayer& layer, GrContext * /* don't use because software compositor */) -> sk_sp { auto surface = CreateRenderSurface( layer, nullptr /* null because software compositor */); @@ -3001,12 +3001,6 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { auto renderered_scene = context.GetNextSceneImage(); latch.Wait(); - - // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all - // platforms. -#if !defined(OS_LINUX) - GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; -#endif // OS_LINUX ASSERT_TRUE(ImageMatchesFixture("verifyb143464703_soft_noxform.png", renderered_scene)); } diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 57598a59e8241..2b822d08fea79 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -110,90 +110,8 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { 'JNI_OnLoad': 'T', '_binary_icudtl_dat_size': 'A', '_binary_icudtl_dat_start': 'D', - // TODO(fxb/47943): Remove these once Clang lld does not expose them. - // arm - '__adddf3': 'T', - '__addsf3': 'T', - '__aeabi_cdcmpeq': 'T', - '__aeabi_cdcmple': 'T', - '__aeabi_cdrcmple': 'T', - '__aeabi_d2lz': 'T', - '__aeabi_d2uiz': 'T', - '__aeabi_d2ulz': 'T', - '__aeabi_dadd': 'T', - '__aeabi_dcmpeq': 'T', - '__aeabi_dcmpge': 'T', - '__aeabi_dcmpgt': 'T', - '__aeabi_dcmple': 'T', - '__aeabi_dcmplt': 'T', - '__aeabi_ddiv': 'T', - '__aeabi_dmul': 'T', - '__aeabi_drsub': 'T', - '__aeabi_dsub': 'T', - '__aeabi_f2d': 'T', - '__aeabi_fadd': 'T', - '__aeabi_frsub': 'T', - '__aeabi_fsub': 'T', - '__aeabi_i2d': 'T', - '__aeabi_i2f': 'T', - '__aeabi_l2d': 'T', - '__aeabi_l2f': 'T', - '__aeabi_lasr': 'T', - '__aeabi_ldivmod': 'T', - '__aeabi_llsl': 'T', - '__aeabi_llsr': 'T', - '__aeabi_ui2d': 'T', - '__aeabi_ui2f': 'T', - '__aeabi_uidiv': 'T', - '__aeabi_uidivmod': 'T', - '__aeabi_ul2d': 'T', - '__aeabi_ul2f': 'T', - '__aeabi_uldivmod': 'T', - '__ashldi3': 'T', - '__ashrdi3': 'T', - '__cmpdf2': 'T', - '__divdf3': 'T', - '__divdi3': 'T', - '__eqdf2': 'T', - '__extendsfdf2': 'T', - '__fixdfdi': 'T', - '__fixunsdfdi': 'T', - '__fixunsdfsi': 'T', - '__floatdidf': 'T', - '__floatdisf': 'T', - '__floatsidf': 'T', - '__floatsisf': 'T', - '__floatundidf': 'T', - '__floatundisf': 'T', - '__floatunsidf': 'T', - '__floatunsisf': 'T', - '__gedf2': 'T', - '__gnu_ldivmod_helper': 'T', - '__gnu_uldivmod_helper': 'T', - '__gtdf2': 'T', - '__ledf2': 'T', - '__lshrdi3': 'T', - '__ltdf2': 'T', - '__muldf3': 'T', - '__nedf2': 'T', - '__subdf3': 'T', - '__subsf3': 'T', - '__udivdi3': 'T', - '__udivsi3': 'T', - // arm64 - '__clz_tab': 'R', - '__udivti3': 'T', - // arm64 && x64 - '__emutls_get_address': 'T', - '__emutls_register_common': 'T', }; - final Map badSymbols = {}; - for (final String key in entryMap.keys) { - if (entryMap[key] != expectedSymbols[key]) { - badSymbols[key] = entryMap[key]; - } - } - if (badSymbols.isNotEmpty) { + if (!const MapEquality().equals(entryMap, expectedSymbols)) { print('ERROR: $libFlutter exports the wrong symbols'); print(' Expected $expectedSymbols'); print(' Library has $entryMap.'); From a82343befa0fd9236690d923ab70520ed7dc3434 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Wed, 1 Apr 2020 18:46:04 -0700 Subject: [PATCH 223/386] Add build_and_test_linux_release presubmit test (#17073) --- .cirrus.yml | 10 ++++++++++ flutter_frontend_server/test/to_string_test.dart | 6 +++++- shell/common/shell_unittests.cc | 12 +++++------- testing/run_tests.py | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 88f3c3e71319e..b2942ded3b186 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -60,10 +60,20 @@ task: cd $ENGINE_PATH/src/out/host_release/ ./txt_benchmarks --benchmark_format=json > txt_benchmarks.json ./fml_benchmarks --benchmark_format=json > fml_benchmarks.json + ./fml_benchmarks --benchmark_format=json > shell_benchmarks.json cd $ENGINE_PATH/src/flutter/testing/benchmark pub get dart bin/parse_and_send.dart ../../../out/host_release/txt_benchmarks.json dart bin/parse_and_send.dart ../../../out/host_release/fml_benchmarks.json + dart bin/parse_and_send.dart ../../../out/host_release/shell_benchmarks.json + - name: build_and_test_linux_release + compile_host_script: | + cd $ENGINE_PATH/src + ./flutter/tools/gn --runtime-mode=release + ninja -C out/host_release + test_host_script: | + cd $ENGINE_PATH/src + ./flutter/testing/run_tests.sh host_release - name: build_and_test_linux_unopt_debug compile_host_script: | cd $ENGINE_PATH/src diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart index 3ee7570973a67..406d595716a71 100644 --- a/flutter_frontend_server/test/to_string_test.dart +++ b/flutter_frontend_server/test/to_string_test.dart @@ -270,9 +270,13 @@ void main(List args) async { ])); final ProcessResult runResult = Process.runSync(dart, [regularDill]); _checkProcessResult(runResult); + String paintString = '"Paint.toString":"Paint(Color(0xffffffff))"'; + if (const bool.fromEnvironment('dart.vm.product', defaultValue: false)) { + paintString = '"Paint.toString":"Instance of \'Paint\'"'; + } expect( runResult.stdout.trim(), - '{"Paint.toString":"Paint(Color(0xffffffff))",' + '{$paintString,' '"Brightness.toString":"Brightness.dark",' '"Foo.toString":"I am a Foo",' '"Keep.toString":"I am a Keep"}', diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 8a7bd5d51dcea..c9c1345541fcc 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -270,21 +270,19 @@ TEST_F(ShellTest, BlacklistedDartVMFlag) { fml::CommandLine::Option("dart-flags", "--verify_after_gc")}; fml::CommandLine command_line("", options, std::vector()); -#if !FLUTTER_RELEASE // Upon encountering a non-whitelisted Dart flag the process terminates. const char* expected = "Encountered blacklisted Dart VM flag: --verify_after_gc"; ASSERT_DEATH(flutter::SettingsFromCommandLine(command_line), expected); -#else - flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); - EXPECT_EQ(settings.dart_flags.size(), 0u); -#endif } TEST_F(ShellTest, WhitelistedDartVMFlag) { const std::vector options = { - fml::CommandLine::Option("dart-flags", - "--max_profile_depth 1,--random_seed 42")}; +#if !FLUTTER_RELEASE + fml::CommandLine::Option("dart-flags", + "--max_profile_depth 1,--random_seed 42") +#endif + }; fml::CommandLine command_line("", options, std::vector()); flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); diff --git a/testing/run_tests.py b/testing/run_tests.py index 083117458d448..68abca30b37de 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -409,7 +409,7 @@ def main(): if 'benchmarks' in types and not IsWindows(): RunEngineBenchmarks(build_dir, engine_filter) - if 'engine' in types or 'font-subset' in types: + if ('engine' in types or 'font-subset' in types) and args.variant != 'host_release': RunCmd(['python', 'test.py'], cwd=font_subset_dir) From 2e5568ede21c02d2f24cdf5fcc1ed5c7100ed501 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 1 Apr 2020 18:51:04 -0700 Subject: [PATCH 224/386] Implement getExtractedText in InputConnectionAdaptor (#17426) --- .../plugin/editing/InputConnectionAdaptor.java | 12 ++++++++++++ .../plugin/editing/InputConnectionAdaptorTest.java | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index 7513b2ffa4d57..93ec036b75ddf 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -21,6 +21,8 @@ import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import io.flutter.Log; @@ -161,6 +163,16 @@ public boolean finishComposingText() { return result; } + // TODO(garyq): Implement a more feature complete version of getExtractedText + @Override + public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { + ExtractedText extractedText = new ExtractedText(); + extractedText.selectionStart = Selection.getSelectionStart(mEditable); + extractedText.selectionEnd = Selection.getSelectionEnd(mEditable); + extractedText.text = mEditable.toString(); + return extractedText; + } + // Detect if the keyboard is a Samsung keyboard, where we apply Samsung-specific hacks to // fix critical bugs that make the keyboard otherwise unusable. See finishComposingText() for // more details. diff --git a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java index 9d8d95095adf5..12d042c0237f6 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java @@ -19,6 +19,7 @@ import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ExtractedText; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.systemchannels.TextInputChannel; @@ -256,6 +257,19 @@ public void testSendKeyEvent_downKeyMovesCaretDown() { assertTrue(Selection.getSelectionStart(editable) > selStart); } + @Test + public void testMethod_getExtractedText() { + int selStart = 5; + Editable editable = sampleEditable(selStart, selStart); + InputConnectionAdaptor adaptor = sampleInputConnectionAdaptor(editable); + + ExtractedText extractedText = adaptor.getExtractedText(null, 0); + + assertEquals(extractedText.text, SAMPLE_TEXT); + assertEquals(extractedText.selectionStart, selStart); + assertEquals(extractedText.selectionEnd, selStart); + } + private static final String SAMPLE_TEXT = "Lorem ipsum dolor sit amet," + "\nconsectetur adipiscing elit."; From 7e1fc4d5b71d5129d5b69615a32f8c608a148b1f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 21:51:08 -0400 Subject: [PATCH 225/386] Roll fuchsia/sdk/core/mac-amd64 from iP6DE... to QgqO4... (#17463) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 17961f25230f6..332c0aee1a6cc 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'iP6DEtpFZFlZM-2hjt5NwAo-ZVhSw0arGA-hX7dRAboC' + 'version': 'QgqO4GD9LyzxC2YeQRVtcEcAmWeTSE60_ikGNyIhJocC' } ], 'condition': 'host_os == "mac"', From 294cc67140469c5d4582dc80c4912ed8af2280cd Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 21:56:03 -0400 Subject: [PATCH 226/386] Roll src/third_party/dart b177e44ffa44..b85c68a73cc2 (21 commits) (#17460) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 332c0aee1a6cc..1d9473a4e2e18 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'b177e44ffa442d4bc5f55af5c60269a1a600e5ab', + 'dart_revision': 'b85c68a73cc2bed3d62999d36707c1006f555b11', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 8bc6bbdd2210a..ffda444626f51 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 65458cd4212e207db2cddcccee596174 +Signature: dee33633c9a5be9d364a4e6f2a224de2 UNUSED LICENSES: From 1ff7fcfa36f640a460121ec543f4854da9b21a48 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 1 Apr 2020 22:01:04 -0400 Subject: [PATCH 227/386] Roll src/third_party/skia bfc09267bdb4..b14b14445884 (13 commits) (#17462) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1d9473a4e2e18..6cbb13b1b156b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'bfc09267bdb446fb31110099f029a846adc001fe', + 'skia_revision': 'b14b1444588430186ed4d56270fa4d5c3399ec50', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 36802d3fc7433..be2b33bd0cb00 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: a8a81675aedb136a08fdd2ffe69e72e9 +Signature: 9d8b38d1dab95f90a0c6ecfc899fb210 UNUSED LICENSES: @@ -1285,6 +1285,7 @@ FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/confetti.json FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/drinks.json FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/lego_loader.json FILE: ../../../third_party/skia/modules/canvaskit/perf/assets/onboarding.json +FILE: ../../../third_party/skia/modules/canvaskit/perf/matrix.bench.js FILE: ../../../third_party/skia/modules/canvaskit/postamble.js FILE: ../../../third_party/skia/modules/canvaskit/preamble.js FILE: ../../../third_party/skia/modules/canvaskit/ready.js @@ -5605,8 +5606,10 @@ FILE: ../../../third_party/skia/modules/skottie/src/animator/Animator.h FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.h FILE: ../../../third_party/skia/modules/skottie/src/animator/Scalar.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/Shape.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/Vec2.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/Vector.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/Vector.h FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Ellipse.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/FillStroke.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Gradient.cpp From 4518c88035b5e70db95af1e0aaeee1e89fd2c19a Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 1 Apr 2020 19:43:40 +0000 Subject: [PATCH 228/386] Manual roll fuchsia/sdk/core/linux-amd64 from nK6R0... to zJlvo... --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 18 ++++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/DEPS b/DEPS index 6cbb13b1b156b..21499d2bf2513 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'nK6R0-Z1LhGfmogq-nAUTlOomxkKUCV00iPPkXSgVgUC' + 'version': 'zJlvoar3sx4OYVmudzR5YziSAeREtThZuCyVjSL3SkYC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 3009d8b178c37..e60b9ef9a6f78 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 9cd367155310be6c188125ba3d3e842d +Signature: e34dc0786388aabfff148b4dd093ca44 UNUSED LICENSES: @@ -1271,8 +1271,6 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/src/vmo/vmo_writer.dar FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/lib/testing.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_inspect/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_logger/meta.json -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/internal/_streaming_intent_handler_impl.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/streaming_intent_handler.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/meta.json FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/agent_interceptor.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/test_harness_fixtures.dart @@ -2599,7 +2597,6 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_logger/lib/src/internal/_stdout_lo FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_logger/lib/src/logger/logger.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/agent.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/codecs.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/entity.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/lifecycle.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/logger.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/module.dart @@ -2607,22 +2604,16 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/service_connection.dar FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/agent/agent.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/agent/internal/_agent_context.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/agent/internal/_agent_impl.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/entity/entity.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/entity/entity_exceptions.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/entity/internal/_entity_impl.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/internal/_component_context.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/lifecycle/internal/_lifecycle_impl.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/lifecycle/lifecycle.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/embedded_module.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/intent.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/intent_handler.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/internal/_fidl_transformers.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/internal/_intent_handler_impl.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/internal/_module_context.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/internal/_module_impl.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/module.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/module_state_exception.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/noop_intent_handler.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/service_connection/agent_service_connection.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/child_view.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_scenic_flutter/lib/child_view_connection.dart @@ -2939,10 +2930,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/agent/agent_context.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/agent/agent_controller/agent_controller.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/component/component_context.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/config/config.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/entity/entity.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/entity/entity_provider.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/entity/entity_reference_factory.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/entity/entity_resolver.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/intent/intent.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/lifecycle/lifecycle.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/module/link_path.fidl @@ -3258,6 +3245,7 @@ FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/noop_view_p FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/view_provider.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/module_interceptor.dart FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular_testing/lib/src/module_with_view_provider_impl.dart +FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/launch.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/power.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/update.dart @@ -3287,6 +3275,8 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/modifiers.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/update.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/url.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/auth.fidl +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/commands_sizing.cc +FILE: ../../../fuchsia/sdk/linux/pkg/scenic_cpp/include/lib/ui/scenic/cpp/commands_sizing.h FILE: ../../../fuchsia/sdk/linux/pkg/zx/include/lib/zx/stream.h FILE: ../../../fuchsia/sdk/linux/pkg/zx/stream.cc ---------------------------------------------------------------------------------------------------- From a3f82ad8125f85b500fc02b33726098de97767ac Mon Sep 17 00:00:00 2001 From: George Wright Date: Wed, 1 Apr 2020 13:54:15 -0700 Subject: [PATCH 229/386] Flush the session after updating the scene --- flow/layers/fuchsia_layer_unittests.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flow/layers/fuchsia_layer_unittests.cc b/flow/layers/fuchsia_layer_unittests.cc index 2a859226ab5ed..ef72830fdf77a 100644 --- a/flow/layers/fuchsia_layer_unittests.cc +++ b/flow/layers/fuchsia_layer_unittests.cc @@ -590,6 +590,8 @@ TEST_F(FuchsiaLayerTest, PhysicalShapeLayersAndChildSceneLayers) { // against the list above. root->UpdateScene(*(test_context->scene_update_context)); + test_context->session->Flush(); + // Run loop until idle, so that the Session receives and processes // its method calls. async_loop_run_until_idle( @@ -751,6 +753,8 @@ TEST_F(FuchsiaLayerTest, Opacity) { // commands against the list above. root->UpdateScene(*(test_context->scene_update_context)); + test_context->session->Flush(); + // Run loop until idle, so that the Session receives and processes // its method calls. async_loop_run_until_idle( From 1a437ac08385635890a704102d90a8a3f86f8870 Mon Sep 17 00:00:00 2001 From: mikejurka Date: Wed, 1 Apr 2020 20:01:52 -0700 Subject: [PATCH 230/386] [fuchsia] Add tests for TransformLayer. (#17439) --- flow/layers/fuchsia_layer_unittests.cc | 118 +++++++++++++++---------- 1 file changed, 73 insertions(+), 45 deletions(-) diff --git a/flow/layers/fuchsia_layer_unittests.cc b/flow/layers/fuchsia_layer_unittests.cc index ef72830fdf77a..ad9971d17e257 100644 --- a/flow/layers/fuchsia_layer_unittests.cc +++ b/flow/layers/fuchsia_layer_unittests.cc @@ -20,6 +20,7 @@ #include "flutter/flow/layers/container_layer.h" #include "flutter/flow/layers/opacity_layer.h" #include "flutter/flow/layers/physical_shape_layer.h" +#include "flutter/flow/layers/transform_layer.h" #include "flutter/flow/view_holder.h" #include "flutter/fml/platform/fuchsia/message_loop_fuchsia.h" #include "flutter/fml/task_runner.h" @@ -47,6 +48,14 @@ class MockSession : public fuchsia::ui::scenic::testing::Session_TestBase { std::to_string(value.value.z) + "}"; } + static std::string QuaternionValueToString( + fuchsia::ui::gfx::QuaternionValue value) { + return "{" + std::to_string(value.value.x) + ", " + + std::to_string(value.value.y) + ", " + + std::to_string(value.value.z) + ", " + + std::to_string(value.value.w) + "}"; + } + static std::string GfxCreateResourceCmdToString( const fuchsia::ui::gfx::CreateResourceCmd& cmd) { std::string id = " id: " + std::to_string(cmd.id); @@ -93,9 +102,10 @@ class MockSession : public fuchsia::ui::scenic::testing::Session_TestBase { " value: " + Vec3ValueToString(cmd.set_translation().value); case fuchsia::ui::gfx::Command::Tag::kSetScale: return "SetScale id: " + std::to_string(cmd.set_scale().id) + - " value: " + Vec3ValueToString(cmd.set_translation().value); + " value: " + Vec3ValueToString(cmd.set_scale().value); case fuchsia::ui::gfx::Command::Tag::kSetRotation: - return "SetRotation id: " + std::to_string(cmd.set_rotation().id); + return "SetRotation id: " + std::to_string(cmd.set_rotation().id) + + " value: " + QuaternionValueToString(cmd.set_rotation().value); case fuchsia::ui::gfx::Command::Tag::kSetOpacity: return "SetOpacity id: " + std::to_string(cmd.set_opacity().node_id) + ", opacity: " + std::to_string(cmd.set_opacity().opacity); @@ -601,13 +611,13 @@ TEST_F(FuchsiaLayerTest, PhysicalShapeLayersAndChildSceneLayers) { EXPECT_EQ(72u, test_context->mock_session.num_enqueued_commands()); } -// Create a hierarchy with OpacityLayers, PhysicalShapeLayers and -// ChildSceneLayers, and inspect the commands sent to Scenic. +// Create a hierarchy with OpacityLayers, TransformLayer, PhysicalShapeLayers +// and ChildSceneLayers, and inspect the commands sent to Scenic. // // We are interested in verifying that the opacity values of children are -// correct. +// correct, and the transform values as well. // -TEST_F(FuchsiaLayerTest, Opacity) { +TEST_F(FuchsiaLayerTest, OpacityAndTransformLayer) { auto test_context = InitTest(); // Root. @@ -625,7 +635,13 @@ TEST_F(FuchsiaLayerTest, Opacity) { std::make_shared(127, SkPoint::Make(0, 0)); opacity_layer1->Add(opacity_layer2); - // Child #1: ChildSceneLayer. + // TransformLayer + SkMatrix translate_and_scale; + translate_and_scale.setScaleTranslate(1.1f, 1.1f, 2.f, 2.f); + auto transform_layer = std::make_shared(translate_and_scale); + opacity_layer2->Add(transform_layer); + + // TransformLayer Child #1: ChildSceneLayer. const zx_koid_t kChildLayerId1 = GetChildLayerId(); auto [unused_view_token1, unused_view_holder_token1] = scenic::ViewTokenPair::New(); @@ -638,14 +654,14 @@ TEST_F(FuchsiaLayerTest, Opacity) { auto child_view1 = std::make_shared( kChildLayerId1, SkPoint::Make(1, 1), SkSize::Make(10, 10), /*hit_testable=*/false); - opacity_layer2->Add(child_view1); + transform_layer->Add(child_view1); - // Child #2: PhysicalShapeLayer. + // TransformLayer Child #2: PhysicalShapeLayer. auto physical_shape1 = std::make_shared( /*color=*/SK_ColorCYAN, /*shadow_color=*/SK_ColorBLACK, /*elevation*/ 23.f, path, Clip::antiAlias); - opacity_layer2->Add(physical_shape1); + transform_layer->Add(physical_shape1); // Preroll. root->Preroll(test_context->preroll_context.get(), SkMatrix()); @@ -670,7 +686,7 @@ TEST_F(FuchsiaLayerTest, Opacity) { expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/1)); expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/2)); expected.push_back(scenic::NewSetLabelCmd(/*id=*/1, "fuchsia test root")); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0})); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/1, {0, 0, 0})); expected.push_back(scenic::NewAddChildCmd(/*id=*/1, /*child_id=*/2)); expected.push_back(scenic::NewSetOpacityCmd(/*id=*/2, kOneMinusEpsilon)); @@ -685,67 +701,79 @@ TEST_F(FuchsiaLayerTest, Opacity) { // Expect no new commands for this. // - // Child #1: ChildSceneLayer. + // TransformLayer + // // expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/3)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/4)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/3)); + expected.push_back(scenic::NewSetLabelCmd(/*id=*/3, "flutter::Transform")); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {2.f, 2.f, 0.f})); + expected.push_back(scenic::NewSetScaleCmd(/*id=*/3, {1.1f, 1.1f, 1.f})); + expected.push_back(scenic::NewSetRotationCmd(/*id=*/3, {0.f, 0.f, 0.f, 1.f})); + + // + // TransformLayer Child #1: ChildSceneLayer. + // + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/4)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/5)); auto [view_token1, view_holder_token1] = scenic::ViewTokenPair::New(); expected.push_back(scenic::NewCreateViewHolderCmd( - /*id=*/5, std::move(view_holder_token1), "")); - expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/3)); - expected.push_back(scenic::NewSetLabelCmd(/*id=*/4, "flutter::ViewHolder")); + /*id=*/6, std::move(view_holder_token1), "")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/5, /*child_id=*/4)); + expected.push_back(scenic::NewSetLabelCmd(/*id=*/5, "flutter::ViewHolder")); + expected.push_back(scenic::NewAddChildCmd(/*id=*/4, /*child_id=*/6)); expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/5)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/4)); // Check opacity value. Extra rounding required because we pass alpha as // a uint/SkAlpha to SceneUpdateContext::Frame. float opacity1 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f); opacity1 = SkScalarRoundToInt(opacity1 * 255) / 255.f; - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/4, opacity1)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/3, {1, 1, -0.1})); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/5, opacity1)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/4, {1, 1, -0.1})); expected.push_back(scenic::NewSetHitTestBehaviorCmd( - /*id=*/3, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); + /*id=*/4, /*ignored*/ fuchsia::ui::gfx::HitTestBehavior::kSuppress)); // - // Child #2: PhysicalShapeLayer + // TransformLayer Child #2: PhysicalShapeLayer // - expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/6)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/2, /*child_id=*/6)); - expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/7)); + expected.push_back(scenic::NewCreateEntityNodeCmd(/*id=*/7)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/3, /*child_id=*/7)); + expected.push_back(scenic::NewCreateOpacityNodeCmdHACK(/*id=*/8)); expected.push_back( - scenic::NewSetLabelCmd(/*id=*/6, "flutter::PhysicalShapeLayer")); + scenic::NewSetLabelCmd(/*id=*/7, "flutter::PhysicalShapeLayer")); expected.push_back(scenic::NewSetTranslationCmd( - /*id=*/6, {0, 0, -kScenicZElevationBetweenLayers})); - expected.push_back(scenic::NewAddChildCmd(/*id=*/6, /*child_id=*/7)); + /*id=*/7, {0, 0, -kScenicZElevationBetweenLayers})); + expected.push_back(scenic::NewAddChildCmd(/*id=*/7, /*child_id=*/8)); // Check opacity value. Extra rounding required because we pass alpha as // a uint/SkAlpha to SceneUpdateContext::Frame. float opacity2 = kOneMinusEpsilon * (127 / 255.f) * (127 / 255.f); opacity2 = SkScalarRoundToInt(opacity2 * 255) / 255.f; - expected.push_back(scenic::NewSetOpacityCmd(/*id=*/7, opacity2)); - expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/6, /*ignored*/ {})); - expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/8)); + expected.push_back(scenic::NewSetOpacityCmd(/*id=*/8, opacity2)); + expected.push_back(scenic::NewSetClipPlanesCmd(/*id=*/7, /*ignored*/ {})); + expected.push_back(scenic::NewCreateShapeNodeCmd(/*id=*/9)); expected.push_back(scenic::NewCreateRectangleCmd( - /*id=*/9, /*width=*/10, /*height=*/10)); - expected.push_back(scenic::NewSetShapeCmd(/*id=*/8, /*shape_id=*/9)); - expected.push_back(scenic::NewSetTranslationCmd(/*id=*/8, {5, 5, 0})); - expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/10)); - expected.push_back(scenic::NewSetMaterialCmd(/*id=*/8, - /*material_id=*/10)); - expected.push_back(scenic::NewAddChildCmd(/*id=*/6, - /*child_id=*/8)); - - expected.push_back(scenic::NewCreateImageCmd(/*id=*/11, 0, 0, {})); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/6)); - expected.push_back(scenic::NewSetColorCmd(/*id=*/10, /*r*/ 255, + /*id=*/10, /*width=*/10, /*height=*/10)); + expected.push_back(scenic::NewSetShapeCmd(/*id=*/9, /*shape_id=*/10)); + expected.push_back(scenic::NewSetTranslationCmd(/*id=*/9, {5, 5, 0})); + expected.push_back(scenic::NewCreateMaterialCmd(/*id=*/11)); + expected.push_back(scenic::NewSetMaterialCmd(/*id=*/9, + /*material_id=*/11)); + expected.push_back(scenic::NewAddChildCmd(/*id=*/7, + /*child_id=*/9)); + + expected.push_back(scenic::NewCreateImageCmd(/*id=*/12, 0, 0, {})); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7)); + expected.push_back(scenic::NewSetColorCmd(/*id=*/11, /*r*/ 255, /*g*/ 255, /*b*/ 255, /*a*/ 63)); expected.push_back( - scenic::NewSetTextureCmd(/*material_id=*/10, /*texture_id=*/11)); + scenic::NewSetTextureCmd(/*material_id=*/11, /*texture_id=*/12)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/11)); expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/10)); expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/9)); expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/8)); - expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/7)); + expected.push_back(scenic::NewReleaseResourceCmd(/*id=*/3)); test_context->mock_session.SetExpectedCommands(std::move(expected)); @@ -761,7 +789,7 @@ TEST_F(FuchsiaLayerTest, Opacity) { async_loop_from_dispatcher(async_get_default_dispatcher())); // Ensure we saw enough commands. - EXPECT_EQ(39u, test_context->mock_session.num_enqueued_commands()); + EXPECT_EQ(46u, test_context->mock_session.num_enqueued_commands()); } } // namespace testing From 39b77188c5425925a72fe0a64d246dbc0b3cdadc Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 1 Apr 2020 20:55:27 -0700 Subject: [PATCH 231/386] Reland Clang 11, roll buildroot to 1bc40a5f916adb93d87e2a2230a0ec43db876c1c, take 3 (#17467) * Reland Clang 11, Roll buildroot to 1bc40a5f916adb93d87e2a2230a0ec43db876c1c (#17457)" (#17464) This reverts commit 9eacd028cdba5c6efef327342dc53fab61474462. * Skip more image tests, use newer dsymutil, add missing symbols --- DEPS | 6 +- .../performance_overlay_layer_unittests.cc | 2 + .../embedder/tests/embedder_unittests.cc | 8 +- sky/tools/create_ios_framework.py | 21 +++-- testing/dart/canvas_test.dart | 4 +- testing/symbols/verify_exported.dart | 87 ++++++++++++++++++- 6 files changed, 114 insertions(+), 14 deletions(-) diff --git a/DEPS b/DEPS index 21499d2bf2513..c567b8f8c9f67 100644 --- a/DEPS +++ b/DEPS @@ -136,7 +136,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '1bc40a5f916adb93d87e2a2230a0ec43db876c1c', # Fuchsia compatibility # @@ -517,8 +517,8 @@ deps = { 'src/buildtools/{host_os}-x64/clang': { 'packages': [ { - 'package': 'fuchsia/clang/${{platform}}', - 'version': 'git_revision:de39621f0f03f20633bdfa50bde97a3908bf6e98' + 'package': 'fuchsia/third_party/clang/${{platform}}', + 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' } ], 'condition': 'host_os == "mac" or host_os == "linux"', diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index cf0cd68853e59..3fcb2e04e835b 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -88,6 +88,8 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; + // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all + // platforms. #if !defined(OS_LINUX) GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 291d8cd266163..78286fcf54152 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1430,7 +1430,7 @@ TEST_F(EmbedderTest, }); context.GetCompositor().SetPlatformViewRendererCallback( - [&](const FlutterLayer& layer, GrContext * + [&](const FlutterLayer& layer, GrContext* /* don't use because software compositor */) -> sk_sp { auto surface = CreateRenderSurface( layer, nullptr /* null because software compositor */); @@ -3001,6 +3001,12 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { auto renderered_scene = context.GetNextSceneImage(); latch.Wait(); + + // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all + // platforms. +#if !defined(OS_LINUX) + GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; +#endif // OS_LINUX ASSERT_TRUE(ImageMatchesFixture("verifyb143464703_soft_noxform.png", renderered_scene)); } diff --git a/sky/tools/create_ios_framework.py b/sky/tools/create_ios_framework.py index bb74044b546e6..8c79921c589f5 100755 --- a/sky/tools/create_ios_framework.py +++ b/sky/tools/create_ios_framework.py @@ -10,6 +10,9 @@ import os +DSYMUTIL = os.path.join(os.path.dirname(__file__), '..', '..', '..', + 'buildtools', 'mac-x64', 'clang', 'bin', 'dsymutil') + def main(): parser = argparse.ArgumentParser(description='Creates Flutter.framework') @@ -33,27 +36,31 @@ def main(): simulator_dylib = os.path.join(simulator_framework, 'Flutter') if not os.path.isdir(arm64_framework): - print 'Cannot find iOS arm64 Framework at', arm64_framework + print('Cannot find iOS arm64 Framework at %s' % arm64_framework) return 1 if not os.path.isdir(armv7_framework): - print 'Cannot find iOS armv7 Framework at', armv7_framework + print('Cannot find iOS armv7 Framework at %s' % armv7_framework) return 1 if not os.path.isdir(simulator_framework): - print 'Cannot find iOS simulator Framework at', simulator_framework + print('Cannot find iOS simulator Framework at %s' % simulator_framework) return 1 if not os.path.isfile(arm64_dylib): - print 'Cannot find iOS arm64 dylib at', arm64_dylib + print('Cannot find iOS arm64 dylib at %s' % arm64_dylib) return 1 if not os.path.isfile(armv7_dylib): - print 'Cannot find iOS armv7 dylib at', armv7_dylib + print('Cannot find iOS armv7 dylib at %s' % armv7_dylib) return 1 if not os.path.isfile(simulator_dylib): - print 'Cannot find iOS simulator dylib at', simulator_dylib + print('Cannot find iOS simulator dylib at %s' % simulator_dylib) + return 1 + + if not os.path.isfile(DSYMUTIL): + print('Cannot find dsymutil at %s' % DSYMUTIL) return 1 shutil.rmtree(fat_framework, True) @@ -76,7 +83,7 @@ def main(): if args.dsym: dsym_out = os.path.splitext(fat_framework)[0] + '.dSYM' - subprocess.check_call(['dsymutil', '-o', dsym_out, linker_out]) + subprocess.check_call([DSYMUTIL, '-o', dsym_out, linker_out]) if args.strip: # copy unstripped diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 85fbe373e5f3d..25d1ed04dc4d6 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -181,7 +181,7 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_gradient.png'); expect(areEqual, true); - }); + }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 test('Simple dithered gradient', () async { Paint.enableDithering = true; @@ -197,5 +197,5 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_dithered_gradient.png'); expect(areEqual, true); - }); + }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 } diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 2b822d08fea79..2bd2d236d0e49 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -110,8 +110,93 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { 'JNI_OnLoad': 'T', '_binary_icudtl_dat_size': 'A', '_binary_icudtl_dat_start': 'D', + // TODO(fxb/47943): Remove these once Clang lld does not expose them. + // arm + '__adddf3': 'T', + '__addsf3': 'T', + '__aeabi_cdcmpeq': 'T', + '__aeabi_cdcmple': 'T', + '__aeabi_cdrcmple': 'T', + '__aeabi_d2lz': 'T', + '__aeabi_d2uiz': 'T', + '__aeabi_d2ulz': 'T', + '__aeabi_dadd': 'T', + '__aeabi_dcmpeq': 'T', + '__aeabi_dcmpge': 'T', + '__aeabi_dcmpgt': 'T', + '__aeabi_dcmple': 'T', + '__aeabi_dcmplt': 'T', + '__aeabi_ddiv': 'T', + '__aeabi_dmul': 'T', + '__aeabi_drsub': 'T', + '__aeabi_dsub': 'T', + '__aeabi_f2d': 'T', + '__aeabi_fadd': 'T', + '__aeabi_frsub': 'T', + '__aeabi_fsub': 'T', + '__aeabi_i2d': 'T', + '__aeabi_i2f': 'T', + '__aeabi_l2d': 'T', + '__aeabi_l2f': 'T', + '__aeabi_lasr': 'T', + '__aeabi_ldivmod': 'T', + '__aeabi_llsl': 'T', + '__aeabi_llsr': 'T', + '__aeabi_ui2d': 'T', + '__aeabi_ui2f': 'T', + '__aeabi_uidiv': 'T', + '__aeabi_uidivmod': 'T', + '__aeabi_ul2d': 'T', + '__aeabi_ul2f': 'T', + '__aeabi_uldivmod': 'T', + '__ashldi3': 'T', + '__ashrdi3': 'T', + '__cmpdf2': 'T', + '__divdf3': 'T', + '__divdi3': 'T', + '__eqdf2': 'T', + '__extendsfdf2': 'T', + '__fixdfdi': 'T', + '__fixunsdfdi': 'T', + '__fixunsdfsi': 'T', + '__floatdidf': 'T', + '__floatdisf': 'T', + '__floatsidf': 'T', + '__floatsisf': 'T', + '__floatundidf': 'T', + '__floatundisf': 'T', + '__floatunsidf': 'T', + '__floatunsisf': 'T', + '__gedf2': 'T', + '__gnu_ldivmod_helper': 'T', + '__gnu_uldivmod_helper': 'T', + '__gtdf2': 'T', + '__ledf2': 'T', + '__lshrdi3': 'T', + '__ltdf2': 'T', + '__muldf3': 'T', + '__nedf2': 'T', + '__subdf3': 'T', + '__subsf3': 'T', + '__udivdi3': 'T', + '__udivsi3': 'T', + // arm64 + '__clz_tab': 'R', + '__udivti3': 'T', + // arm64 && x64 + '__emutls_get_address': 'T', + '__emutls_register_common': 'T', + // jit x86 + '__moddi3': 'T', + '__umoddi3': 'T', }; - if (!const MapEquality().equals(entryMap, expectedSymbols)) { + final Map badSymbols = {}; + for (final String key in entryMap.keys) { + if (entryMap[key] != expectedSymbols[key]) { + badSymbols[key] = entryMap[key]; + } + } + if (badSymbols.isNotEmpty) { print('ERROR: $libFlutter exports the wrong symbols'); print(' Expected $expectedSymbols'); print(' Library has $entryMap.'); From d1ae5cbc7af05e3e667adfe11a6392f07071e83e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 00:26:03 -0400 Subject: [PATCH 232/386] Roll src/third_party/skia b14b14445884..46aacc771012 (5 commits) (#17466) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c567b8f8c9f67..2743989f4001d 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'b14b1444588430186ed4d56270fa4d5c3399ec50', + 'skia_revision': '46aacc771012937cbc666a923f64f04fa9154809', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index be2b33bd0cb00..714890e3c6127 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9d8b38d1dab95f90a0c6ecfc899fb210 +Signature: 9014cee3d7b2834aea36d0efabde7cce UNUSED LICENSES: From 50c8562024b8cececb35b7502fef97b818807218 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 01:41:03 -0400 Subject: [PATCH 233/386] Roll src/third_party/dart b85c68a73cc2..6e53ac6e4c7b (3 commits) (#17469) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 2743989f4001d..6922d462ed873 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'b85c68a73cc2bed3d62999d36707c1006f555b11', + 'dart_revision': '6e53ac6e4c7b3d94de5ee0370a538b03dafded1c', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index ffda444626f51..f40e1937c1b59 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: dee33633c9a5be9d364a4e6f2a224de2 +Signature: 5b3f003586ed2be66f2d786535f46b98 UNUSED LICENSES: From 3b6e61d788c1af106f86863b60cab60c8965c3e5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 1 Apr 2020 23:07:05 -0700 Subject: [PATCH 234/386] Roll Android NDK to r21.0.6113669 (#17470) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 6922d462ed873..4db1df85c9fb3 100644 --- a/DEPS +++ b/DEPS @@ -428,7 +428,7 @@ deps = { 'packages': [ { 'package': 'flutter/android/ndk/${{platform}}', - 'version': 'version:r19b' + 'version': 'version:r21.0.6113669' } ], 'condition': 'download_android_deps', From 374601eaa924c5b1e59f218ed9f96d25c938a3aa Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 02:56:03 -0400 Subject: [PATCH 235/386] Roll fuchsia/sdk/core/linux-amd64 from zJlvo... to v61zg... (#17471) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 4db1df85c9fb3..67299a16e5a15 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'zJlvoar3sx4OYVmudzR5YziSAeREtThZuCyVjSL3SkYC' + 'version': 'v61zgYIe9NuuksbE4M1t2gGUKFkkbftlCx7cF7xRPOcC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index e60b9ef9a6f78..444c4c408b4b9 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: e34dc0786388aabfff148b4dd093ca44 +Signature: 18c6c9c5873393a0ab127ace3b75f16e UNUSED LICENSES: @@ -3268,6 +3268,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media.target/target_discovery.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/activity_reporter.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/profile_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/memorypressure.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/session/session_restart_controller.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/night_mode.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/events.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/keyboard.fidl From d8a4f16861c83364ba5b654308083252da7f2c4f Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 03:01:04 -0400 Subject: [PATCH 236/386] Roll src/third_party/skia 46aacc771012..6c51bf61e283 (3 commits) (#17472) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 67299a16e5a15..346e9416a618f 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '46aacc771012937cbc666a923f64f04fa9154809', + 'skia_revision': '6c51bf61e283a3daee085249570d187456c61d9f', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 714890e3c6127..cbcf02ad33c70 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9014cee3d7b2834aea36d0efabde7cce +Signature: 254c78f8675dbc34309788f0baf604b9 UNUSED LICENSES: From d1491d116d18fb0b779087da518754a3285387bb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 07:11:03 -0400 Subject: [PATCH 237/386] Roll src/third_party/dart 6e53ac6e4c7b..5a965d5d0fef (2 commits) (#17476) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 346e9416a618f..088ecd8609a93 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '6e53ac6e4c7b3d94de5ee0370a538b03dafded1c', + 'dart_revision': '5a965d5d0fef5ee17cbf3dcde94570fe1d6f0b94', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index f40e1937c1b59..2d3bbdc8cd13b 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 5b3f003586ed2be66f2d786535f46b98 +Signature: f5bf65965fda85309d68bec5dbe382e7 UNUSED LICENSES: From 1adfd639b465ddc33bc5f153fe3182f21d38a830 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 09:56:03 -0400 Subject: [PATCH 238/386] Roll src/third_party/skia 6c51bf61e283..8ce842d38d0b (1 commits) (#17477) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 088ecd8609a93..b1c6464487c95 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '6c51bf61e283a3daee085249570d187456c61d9f', + 'skia_revision': '8ce842d38d0b32149e874d6855c91e8c68ba65a7', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index cbcf02ad33c70..d82d32b28799a 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 254c78f8675dbc34309788f0baf604b9 +Signature: 700acd3a99866d4d13b956193c9255f1 UNUSED LICENSES: From 573e4e261caefaa466825114d379b95ba3d541bb Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 12:56:03 -0400 Subject: [PATCH 239/386] Roll src/third_party/dart 5a965d5d0fef..fae35fca47c9 (7 commits) (#17478) --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index b1c6464487c95..b85ec31437a7a 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '5a965d5d0fef5ee17cbf3dcde94570fe1d6f0b94', + 'dart_revision': 'fae35fca47c9fed7a2339cf2e9535d526146ddd2', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -76,7 +76,7 @@ vars = { 'dart_pedantic_tag': 'v1.8.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', - 'dart_pub_rev': '978cc9c4be7d8978348e56d4ae82be8257a59579', + 'dart_pub_rev': '3606265962da4248d34d352aa3d170aae4496a90', 'dart_pub_semver_tag': 'v1.4.4', 'dart_quiver-dart_tag': '2.0.0+1', 'dart_resource_rev': 'f8e37558a1c4f54550aa463b88a6a831e3e33cd6', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 2d3bbdc8cd13b..5622db71b633c 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f5bf65965fda85309d68bec5dbe382e7 +Signature: 6a8cdbfab22fe5596c1dd7b9f6696c39 UNUSED LICENSES: From 8166a91bca11b83c16fba8f8fd407e937a2ac89c Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 2 Apr 2020 10:01:26 -0700 Subject: [PATCH 240/386] Revert "Reland Clang 11, roll buildroot to 1bc40a5f916adb93d87e2a2230a0ec43db876c1c, take 3 (#17467)" (#17482) This reverts commit 39b77188c5425925a72fe0a64d246dbc0b3cdadc. --- DEPS | 6 +- .../performance_overlay_layer_unittests.cc | 2 - .../embedder/tests/embedder_unittests.cc | 8 +- sky/tools/create_ios_framework.py | 21 ++--- testing/dart/canvas_test.dart | 4 +- testing/symbols/verify_exported.dart | 87 +------------------ 6 files changed, 14 insertions(+), 114 deletions(-) diff --git a/DEPS b/DEPS index b85ec31437a7a..583daa4f7137c 100644 --- a/DEPS +++ b/DEPS @@ -136,7 +136,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '1bc40a5f916adb93d87e2a2230a0ec43db876c1c', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', # Fuchsia compatibility # @@ -517,8 +517,8 @@ deps = { 'src/buildtools/{host_os}-x64/clang': { 'packages': [ { - 'package': 'fuchsia/third_party/clang/${{platform}}', - 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' + 'package': 'fuchsia/clang/${{platform}}', + 'version': 'git_revision:de39621f0f03f20633bdfa50bde97a3908bf6e98' } ], 'condition': 'host_os == "mac" or host_os == "linux"', diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index 3fcb2e04e835b..cf0cd68853e59 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -88,8 +88,6 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; - // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all - // platforms. #if !defined(OS_LINUX) GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 78286fcf54152..291d8cd266163 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1430,7 +1430,7 @@ TEST_F(EmbedderTest, }); context.GetCompositor().SetPlatformViewRendererCallback( - [&](const FlutterLayer& layer, GrContext* + [&](const FlutterLayer& layer, GrContext * /* don't use because software compositor */) -> sk_sp { auto surface = CreateRenderSurface( layer, nullptr /* null because software compositor */); @@ -3001,12 +3001,6 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { auto renderered_scene = context.GetNextSceneImage(); latch.Wait(); - - // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all - // platforms. -#if !defined(OS_LINUX) - GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; -#endif // OS_LINUX ASSERT_TRUE(ImageMatchesFixture("verifyb143464703_soft_noxform.png", renderered_scene)); } diff --git a/sky/tools/create_ios_framework.py b/sky/tools/create_ios_framework.py index 8c79921c589f5..bb74044b546e6 100755 --- a/sky/tools/create_ios_framework.py +++ b/sky/tools/create_ios_framework.py @@ -10,9 +10,6 @@ import os -DSYMUTIL = os.path.join(os.path.dirname(__file__), '..', '..', '..', - 'buildtools', 'mac-x64', 'clang', 'bin', 'dsymutil') - def main(): parser = argparse.ArgumentParser(description='Creates Flutter.framework') @@ -36,31 +33,27 @@ def main(): simulator_dylib = os.path.join(simulator_framework, 'Flutter') if not os.path.isdir(arm64_framework): - print('Cannot find iOS arm64 Framework at %s' % arm64_framework) + print 'Cannot find iOS arm64 Framework at', arm64_framework return 1 if not os.path.isdir(armv7_framework): - print('Cannot find iOS armv7 Framework at %s' % armv7_framework) + print 'Cannot find iOS armv7 Framework at', armv7_framework return 1 if not os.path.isdir(simulator_framework): - print('Cannot find iOS simulator Framework at %s' % simulator_framework) + print 'Cannot find iOS simulator Framework at', simulator_framework return 1 if not os.path.isfile(arm64_dylib): - print('Cannot find iOS arm64 dylib at %s' % arm64_dylib) + print 'Cannot find iOS arm64 dylib at', arm64_dylib return 1 if not os.path.isfile(armv7_dylib): - print('Cannot find iOS armv7 dylib at %s' % armv7_dylib) + print 'Cannot find iOS armv7 dylib at', armv7_dylib return 1 if not os.path.isfile(simulator_dylib): - print('Cannot find iOS simulator dylib at %s' % simulator_dylib) - return 1 - - if not os.path.isfile(DSYMUTIL): - print('Cannot find dsymutil at %s' % DSYMUTIL) + print 'Cannot find iOS simulator dylib at', simulator_dylib return 1 shutil.rmtree(fat_framework, True) @@ -83,7 +76,7 @@ def main(): if args.dsym: dsym_out = os.path.splitext(fat_framework)[0] + '.dSYM' - subprocess.check_call([DSYMUTIL, '-o', dsym_out, linker_out]) + subprocess.check_call(['dsymutil', '-o', dsym_out, linker_out]) if args.strip: # copy unstripped diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 25d1ed04dc4d6..85fbe373e5f3d 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -181,7 +181,7 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_gradient.png'); expect(areEqual, true); - }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 + }); test('Simple dithered gradient', () async { Paint.enableDithering = true; @@ -197,5 +197,5 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_dithered_gradient.png'); expect(areEqual, true); - }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 + }); } diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 2bd2d236d0e49..2b822d08fea79 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -110,93 +110,8 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { 'JNI_OnLoad': 'T', '_binary_icudtl_dat_size': 'A', '_binary_icudtl_dat_start': 'D', - // TODO(fxb/47943): Remove these once Clang lld does not expose them. - // arm - '__adddf3': 'T', - '__addsf3': 'T', - '__aeabi_cdcmpeq': 'T', - '__aeabi_cdcmple': 'T', - '__aeabi_cdrcmple': 'T', - '__aeabi_d2lz': 'T', - '__aeabi_d2uiz': 'T', - '__aeabi_d2ulz': 'T', - '__aeabi_dadd': 'T', - '__aeabi_dcmpeq': 'T', - '__aeabi_dcmpge': 'T', - '__aeabi_dcmpgt': 'T', - '__aeabi_dcmple': 'T', - '__aeabi_dcmplt': 'T', - '__aeabi_ddiv': 'T', - '__aeabi_dmul': 'T', - '__aeabi_drsub': 'T', - '__aeabi_dsub': 'T', - '__aeabi_f2d': 'T', - '__aeabi_fadd': 'T', - '__aeabi_frsub': 'T', - '__aeabi_fsub': 'T', - '__aeabi_i2d': 'T', - '__aeabi_i2f': 'T', - '__aeabi_l2d': 'T', - '__aeabi_l2f': 'T', - '__aeabi_lasr': 'T', - '__aeabi_ldivmod': 'T', - '__aeabi_llsl': 'T', - '__aeabi_llsr': 'T', - '__aeabi_ui2d': 'T', - '__aeabi_ui2f': 'T', - '__aeabi_uidiv': 'T', - '__aeabi_uidivmod': 'T', - '__aeabi_ul2d': 'T', - '__aeabi_ul2f': 'T', - '__aeabi_uldivmod': 'T', - '__ashldi3': 'T', - '__ashrdi3': 'T', - '__cmpdf2': 'T', - '__divdf3': 'T', - '__divdi3': 'T', - '__eqdf2': 'T', - '__extendsfdf2': 'T', - '__fixdfdi': 'T', - '__fixunsdfdi': 'T', - '__fixunsdfsi': 'T', - '__floatdidf': 'T', - '__floatdisf': 'T', - '__floatsidf': 'T', - '__floatsisf': 'T', - '__floatundidf': 'T', - '__floatundisf': 'T', - '__floatunsidf': 'T', - '__floatunsisf': 'T', - '__gedf2': 'T', - '__gnu_ldivmod_helper': 'T', - '__gnu_uldivmod_helper': 'T', - '__gtdf2': 'T', - '__ledf2': 'T', - '__lshrdi3': 'T', - '__ltdf2': 'T', - '__muldf3': 'T', - '__nedf2': 'T', - '__subdf3': 'T', - '__subsf3': 'T', - '__udivdi3': 'T', - '__udivsi3': 'T', - // arm64 - '__clz_tab': 'R', - '__udivti3': 'T', - // arm64 && x64 - '__emutls_get_address': 'T', - '__emutls_register_common': 'T', - // jit x86 - '__moddi3': 'T', - '__umoddi3': 'T', }; - final Map badSymbols = {}; - for (final String key in entryMap.keys) { - if (entryMap[key] != expectedSymbols[key]) { - badSymbols[key] = entryMap[key]; - } - } - if (badSymbols.isNotEmpty) { + if (!const MapEquality().equals(entryMap, expectedSymbols)) { print('ERROR: $libFlutter exports the wrong symbols'); print(' Expected $expectedSymbols'); print(' Library has $entryMap.'); From 3002e25eb41a749b7047144f75f1e278b1cef946 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 2 Apr 2020 10:06:06 -0700 Subject: [PATCH 241/386] Initialize locale from FlutterEngine (#17473) * Initialize locale from FlutterEngine --- .../ios/framework/Source/FlutterEngine.mm | 48 +++++++++++++++++++ .../framework/Source/FlutterViewController.mm | 48 ------------------- testing/scenario_app/lib/main.dart | 1 + 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index cf636aef8a3ea..f5fdebdf7eb35 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -122,6 +122,11 @@ - (instancetype)initWithName:(NSString*)labelPrefix name:UIApplicationWillResignActiveNotification object:nil]; + [center addObserver:self + selector:@selector(onLocaleUpdated:) + name:NSCurrentLocaleDidChangeNotification + object:nil]; + return self; } @@ -494,6 +499,7 @@ - (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI { << entrypoint.UTF8String; } else { [self setupChannels]; + [self onLocaleUpdated:nil]; if (!_platformViewsController) { _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); } @@ -710,6 +716,48 @@ - (void)setIsGpuDisabled:(BOOL)value { _isGpuDisabled = value; } +#pragma mark - Locale updates + +- (void)onLocaleUpdated:(NSNotification*)notification { + NSArray* preferredLocales = [NSLocale preferredLanguages]; + NSMutableArray* data = [[NSMutableArray new] autorelease]; + + // Force prepend the [NSLocale currentLocale] to the front of the list + // to ensure we are including the full default locale. preferredLocales + // is not guaranteed to include anything beyond the languageCode. + NSLocale* currentLocale = [NSLocale currentLocale]; + NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; + NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; + NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; + NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; + if (languageCode) { + [data addObject:languageCode]; + [data addObject:(countryCode ? countryCode : @"")]; + [data addObject:(scriptCode ? scriptCode : @"")]; + [data addObject:(variantCode ? variantCode : @"")]; + } + + // Add any secondary locales/languages to the list. + for (NSString* localeID in preferredLocales) { + NSLocale* currentLocale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease]; + NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; + NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; + NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; + NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; + if (!languageCode) { + continue; + } + [data addObject:languageCode]; + [data addObject:(countryCode ? countryCode : @"")]; + [data addObject:(scriptCode ? scriptCode : @"")]; + [data addObject:(variantCode ? variantCode : @"")]; + } + if (data.count == 0) { + return; + } + [self.localizationChannel invokeMethod:@"setLocale" arguments:data]; +} + @end @implementation FlutterEngineRegistrar { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 7f8ba6b1830d1..d3613713b1641 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -279,11 +279,6 @@ - (void)setupNotificationCenterObservers { name:UIKeyboardWillHideNotification object:nil]; - [center addObserver:self - selector:@selector(onLocaleUpdated:) - name:NSCurrentLocaleDidChangeNotification - object:nil]; - [center addObserver:self selector:@selector(onAccessibilityStatusChanged:) name:UIAccessibilityVoiceOverStatusChanged @@ -595,7 +590,6 @@ - (void)viewWillAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidAppear"); - [self onLocaleUpdated:nil]; [self onUserSettingsChanged:nil]; [self onAccessibilityStatusChanged:nil]; [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"]; @@ -1054,48 +1048,6 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification { #endif } -#pragma mark - Locale updates - -- (void)onLocaleUpdated:(NSNotification*)notification { - NSArray* preferredLocales = [NSLocale preferredLanguages]; - NSMutableArray* data = [[NSMutableArray new] autorelease]; - - // Force prepend the [NSLocale currentLocale] to the front of the list - // to ensure we are including the full default locale. preferredLocales - // is not guaranteed to include anything beyond the languageCode. - NSLocale* currentLocale = [NSLocale currentLocale]; - NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; - NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; - NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; - NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; - if (languageCode) { - [data addObject:languageCode]; - [data addObject:(countryCode ? countryCode : @"")]; - [data addObject:(scriptCode ? scriptCode : @"")]; - [data addObject:(variantCode ? variantCode : @"")]; - } - - // Add any secondary locales/languages to the list. - for (NSString* localeID in preferredLocales) { - NSLocale* currentLocale = [[[NSLocale alloc] initWithLocaleIdentifier:localeID] autorelease]; - NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode]; - NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; - NSString* scriptCode = [currentLocale objectForKey:NSLocaleScriptCode]; - NSString* variantCode = [currentLocale objectForKey:NSLocaleVariantCode]; - if (!languageCode) { - continue; - } - [data addObject:languageCode]; - [data addObject:(countryCode ? countryCode : @"")]; - [data addObject:(scriptCode ? scriptCode : @"")]; - [data addObject:(variantCode ? variantCode : @"")]; - } - if (data.count == 0) { - return; - } - [[_engine.get() localizationChannel] invokeMethod:@"setLocale" arguments:data]; -} - #pragma mark - Set user settings - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection { diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 0ab3f7c5352f1..17e56934cded8 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -43,6 +43,7 @@ Map _scenarios = { Scenario _currentScenario = _scenarios['animated_color_square']; void main() { + assert(window.locale != null); window ..onPlatformMessage = _handlePlatformMessage ..onBeginFrame = _onBeginFrame From 49d65b45cfee10b61c4c5be871916b7cdfcba4df Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 13:51:02 -0400 Subject: [PATCH 242/386] Roll src/third_party/skia 8ce842d38d0b..8afde5f39508 (9 commits) (#17480) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 583daa4f7137c..1ad254a2cba62 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '8ce842d38d0b32149e874d6855c91e8c68ba65a7', + 'skia_revision': '8afde5f395088351ea43045a380e2feb55bf88ea', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index d82d32b28799a..879f13e9efa7e 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 700acd3a99866d4d13b956193c9255f1 +Signature: 9d9bbbc53975fe3640278f517c6d165c UNUSED LICENSES: @@ -3233,7 +3233,6 @@ FILE: ../../../third_party/skia/gm/scaledemoji.cpp FILE: ../../../third_party/skia/gm/scaledemoji_rendering.cpp FILE: ../../../third_party/skia/gm/shadermaskfilter.cpp FILE: ../../../third_party/skia/gm/sharedcorners.cpp -FILE: ../../../third_party/skia/gm/skinning.cpp FILE: ../../../third_party/skia/gm/trickycubicstrokes.cpp FILE: ../../../third_party/skia/gm/unpremul.cpp FILE: ../../../third_party/skia/gm/wacky_yuv_formats.cpp From e35bb4594aed5ad03cab43237b2bf557b9584769 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Thu, 2 Apr 2020 11:21:04 -0700 Subject: [PATCH 243/386] fix typos in switch descriptions (#17475) --- shell/common/switches.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shell/common/switches.h b/shell/common/switches.h index e509b5cfd6160..abf1b3ae2997a 100644 --- a/shell/common/switches.h +++ b/shell/common/switches.h @@ -95,13 +95,13 @@ DEF_SWITCH(EndlessTraceBuffer, "indefinitely however.") DEF_SWITCH(EnableSoftwareRendering, "enable-software-rendering", - "Enable rendering using the Skia software backend. This is useful" - "when testing Flutter on emulators. By default, Flutter will" + "Enable rendering using the Skia software backend. This is useful " + "when testing Flutter on emulators. By default, Flutter will " "attempt to either use OpenGL or Vulkan.") DEF_SWITCH(SkiaDeterministicRendering, "skia-deterministic-rendering", - "Skips the call to SkGraphics::Init(), thus avoiding swapping out" - "some Skia function pointers based on available CPU features. This" + "Skips the call to SkGraphics::Init(), thus avoiding swapping out " + "some Skia function pointers based on available CPU features. This " "is used to obtain 100% deterministic behavior in Skia rendering.") DEF_SWITCH(FlutterAssetsDir, "flutter-assets-dir", From f56e678e7fa9f1249253f06c8c0797ddc67a9df2 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 14:51:02 -0400 Subject: [PATCH 244/386] Roll fuchsia/sdk/core/mac-amd64 from QgqO4... to 6V5BR... (#17481) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 1ad254a2cba62..f55f2daf51f74 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'QgqO4GD9LyzxC2YeQRVtcEcAmWeTSE60_ikGNyIhJocC' + 'version': '6V5BRZbu7X78QeWu5MLPsTSEe87gD9AjXtxEhUkf4JAC' } ], 'condition': 'host_os == "mac"', From 0b3f2d3e94e90485b89dc8167c3bcb2e12d8863e Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Thu, 2 Apr 2020 13:00:29 -0700 Subject: [PATCH 245/386] Always cache SkSL when using the Metal backend. (#17468) There is no ability to compile shaders online and cache those binaries when using the Metal backend. SkSL caching must always be used. --- .../platform/darwin/ios/ios_context_metal.mm | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/ios_context_metal.mm b/shell/platform/darwin/ios/ios_context_metal.mm index eb16aab4a7008..ea5de5988c3b5 100644 --- a/shell/platform/darwin/ios/ios_context_metal.mm +++ b/shell/platform/darwin/ios/ios_context_metal.mm @@ -5,10 +5,22 @@ #include "flutter/shell/platform/darwin/ios/ios_context_metal.h" #include "flutter/fml/logging.h" +#include "flutter/shell/common/persistent_cache.h" #include "flutter/shell/platform/darwin/ios/ios_external_texture_metal.h" +#include "third_party/skia/include/gpu/GrContextOptions.h" namespace flutter { +static GrContextOptions CreateMetalGrContextOptions() { + GrContextOptions options = {}; + if (PersistentCache::cache_sksl()) { + options.fShaderCacheStrategy = GrContextOptions::ShaderCacheStrategy::kSkSL; + } + PersistentCache::MarkStrategySet(); + options.fPersistentCache = PersistentCache::GetCacheForProcess(); + return options; +} + IOSContextMetal::IOSContextMetal() { device_.reset([MTLCreateSystemDefaultDevice() retain]); if (!device_) { @@ -25,16 +37,20 @@ [main_queue_ setLabel:@"Flutter Main Queue"]; + const auto& context_options = CreateMetalGrContextOptions(); + // Skia expect arguments to `MakeMetal` transfer ownership of the reference in for release later // when the GrContext is collected. - main_context_ = GrContext::MakeMetal([device_ retain], [main_queue_ retain]); - resource_context_ = GrContext::MakeMetal([device_ retain], [main_queue_ retain]); + main_context_ = GrContext::MakeMetal([device_ retain], [main_queue_ retain], context_options); + resource_context_ = GrContext::MakeMetal([device_ retain], [main_queue_ retain], context_options); if (!main_context_ || !resource_context_) { FML_DLOG(ERROR) << "Could not create Skia Metal contexts."; return; } + resource_context_->setResourceCacheLimits(0u, 0u); + CVMetalTextureCacheRef texture_cache_raw = NULL; auto cv_return = CVMetalTextureCacheCreate(kCFAllocatorDefault, // allocator NULL, // cache attributes (NULL default) From ff62dec5c96d3560b36ca0a3924d28a3c97b7788 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 2 Apr 2020 13:04:49 -0700 Subject: [PATCH 246/386] Roll to clang11, mark 4 (#17483) * "Reland Clang 11, roll buildroot to 1bc40a5f916adb93d87e2a2230a0ec43db876c1c, take 4 (#17467)" (#17482)" This reverts commit 8166a91bca11b83c16fba8f8fd407e937a2ac89c. * format code * Roll buildroot to 3a27de1c5bb0f50be7d3efe3e00de4f6068c9f30 --- DEPS | 6 +- ci/format.sh | 2 +- .../performance_overlay_layer_unittests.cc | 2 + shell/common/shell.cc | 6 +- .../ios/framework/Source/FlutterEngine.mm | 4 +- .../framework/Source/FlutterViewController.mm | 12 +-- .../framework/Source/accessibility_bridge.mm | 26 +++--- .../Source/accessibility_text_entry.mm | 4 +- .../platform/darwin/ios/platform_view_ios.mm | 2 +- .../embedder/tests/embedder_unittests.cc | 8 +- .../platform/fuchsia/flutter/vsync_waiter.cc | 2 +- .../fuchsia/flutter/vulkan_surface.cc | 6 +- sky/tools/create_ios_framework.py | 21 +++-- testing/dart/canvas_test.dart | 4 +- testing/symbols/verify_exported.dart | 87 ++++++++++++++++++- third_party/txt/src/minikin/HbFontCache.cpp | 2 +- 16 files changed, 147 insertions(+), 47 deletions(-) diff --git a/DEPS b/DEPS index f55f2daf51f74..cec9991598d1f 100644 --- a/DEPS +++ b/DEPS @@ -136,7 +136,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + 'a0fb98af7b6f50f17b47ab7968f3f5664d0d6316', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '3a27de1c5bb0f50be7d3efe3e00de4f6068c9f30', # Fuchsia compatibility # @@ -517,8 +517,8 @@ deps = { 'src/buildtools/{host_os}-x64/clang': { 'packages': [ { - 'package': 'fuchsia/clang/${{platform}}', - 'version': 'git_revision:de39621f0f03f20633bdfa50bde97a3908bf6e98' + 'package': 'fuchsia/third_party/clang/${{platform}}', + 'version': 'git_revision:7e9747b50bcb1be28d4a3236571e8050835497a6' } ], 'condition': 'host_os == "mac" or host_os == "linux"', diff --git a/ci/format.sh b/ci/format.sh index 254d38fbc43a1..979cf684f2b0a 100755 --- a/ci/format.sh +++ b/ci/format.sh @@ -42,7 +42,7 @@ fi; BASE_SHA="$(git fetch $UPSTREAM master > /dev/null 2>&1 && \ (git merge-base --fork-point FETCH_HEAD HEAD || git merge-base FETCH_HEAD HEAD))" -CLANG_FILES_TO_CHECK="$(git diff $DIFF_OPTS $BASE_SHA -- $CLANG_FILETYPES)" +CLANG_FILES_TO_CHECK="$(git ls-files $CLANG_FILETYPES)" FAILED_CHECKS=0 for f in $CLANG_FILES_TO_CHECK; do set +e diff --git a/flow/layers/performance_overlay_layer_unittests.cc b/flow/layers/performance_overlay_layer_unittests.cc index cf0cd68853e59..3fcb2e04e835b 100644 --- a/flow/layers/performance_overlay_layer_unittests.cc +++ b/flow/layers/performance_overlay_layer_unittests.cc @@ -88,6 +88,8 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { << "Please either set --golden-dir, or make sure that the unit test is " << "run from the right directory (e.g., flutter/engine/src)."; + // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all + // platforms. #if !defined(OS_LINUX) GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; #endif // OS_LINUX diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 9125f8881b272..c09da8e4287c0 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -606,7 +606,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { // a synchronous fashion. fml::AutoResetWaitableEvent latch; auto raster_task = - fml::MakeCopyable([& waiting_for_first_frame = waiting_for_first_frame_, + fml::MakeCopyable([&waiting_for_first_frame = waiting_for_first_frame_, rasterizer = rasterizer_->GetWeakPtr(), // surface = std::move(surface), // &latch]() mutable { @@ -955,7 +955,7 @@ void Shell::OnAnimatorDraw(fml::RefPtr> pipeline) { FML_DCHECK(is_setup_); task_runners_.GetRasterTaskRunner()->PostTask( - [& waiting_for_first_frame = waiting_for_first_frame_, + [&waiting_for_first_frame = waiting_for_first_frame_, &waiting_for_first_frame_condition = waiting_for_first_frame_condition_, rasterizer = rasterizer_->GetWeakPtr(), pipeline = std::move(pipeline)]() { @@ -1447,7 +1447,7 @@ fml::Status Shell::WaitForFirstFrame(fml::TimeDelta timeout) { std::unique_lock lock(waiting_for_first_frame_mutex_); bool success = waiting_for_first_frame_condition_.wait_for( lock, std::chrono::milliseconds(timeout.ToMilliseconds()), - [& waiting_for_first_frame = waiting_for_first_frame_] { + [&waiting_for_first_frame = waiting_for_first_frame_] { return !waiting_for_first_frame.load(); }); if (success) { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index f5fdebdf7eb35..bfb4a2d2ff8cd 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -824,8 +824,8 @@ - (void)registerViewFactory:(NSObject*)factory withId:(NSString*)factoryId gestureRecognizersBlockingPolicy: (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy { - [_flutterEngine platformViewsController] -> RegisterViewFactory(factory, factoryId, - gestureRecognizersBlockingPolicy); + [_flutterEngine platformViewsController]->RegisterViewFactory(factory, factoryId, + gestureRecognizersBlockingPolicy); } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index d3613713b1641..e6906d8d07591 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -543,14 +543,14 @@ - (void)surfaceUpdated:(BOOL)appeared { // thread. if (appeared) { [self installFirstFrameCallback]; - [_engine.get() platformViewsController] -> SetFlutterView(_flutterView.get()); - [_engine.get() platformViewsController] -> SetFlutterViewController(self); - [_engine.get() platformView] -> NotifyCreated(); + [_engine.get() platformViewsController]->SetFlutterView(_flutterView.get()); + [_engine.get() platformViewsController]->SetFlutterViewController(self); + [_engine.get() platformView]->NotifyCreated(); } else { self.displayingFlutterUI = NO; - [_engine.get() platformView] -> NotifyDestroyed(); - [_engine.get() platformViewsController] -> SetFlutterView(nullptr); - [_engine.get() platformViewsController] -> SetFlutterViewController(nullptr); + [_engine.get() platformView]->NotifyDestroyed(); + [_engine.get() platformViewsController]->SetFlutterView(nullptr); + [_engine.get() platformViewsController]->SetFlutterViewController(nullptr); } } diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index b07a32989f18e..53f097e65b029 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -288,8 +288,8 @@ - (BOOL)onCustomAccessibilityAction:(FlutterCustomAccessibilityAction*)action { args.push_back(action_id >> 8); args.push_back(action_id >> 16); args.push_back(action_id >> 24); - [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kCustomAction, - std::move(args)); + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kCustomAction, + std::move(args)); return YES; } @@ -385,7 +385,7 @@ - (CGRect)globalRect { CGFloat scale = [[[self bridge]->view() window] screen].scale; auto result = CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale); - return UIAccessibilityConvertFrameToScreenCoordinates(result, [self bridge] -> view()); + return UIAccessibilityConvertFrameToScreenCoordinates(result, [self bridge]->view()); } #pragma mark - UIAccessibilityElement protocol @@ -413,7 +413,7 @@ - (BOOL)accessibilityActivate { return NO; if (![self node].HasAction(flutter::SemanticsAction::kTap)) return NO; - [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kTap); + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kTap); return YES; } @@ -422,7 +422,7 @@ - (void)accessibilityIncrement { return; if ([self node].HasAction(flutter::SemanticsAction::kIncrease)) { [self node].value = [self node].increasedValue; - [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kIncrease); + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kIncrease); } } @@ -431,7 +431,7 @@ - (void)accessibilityDecrement { return; if ([self node].HasAction(flutter::SemanticsAction::kDecrease)) { [self node].value = [self node].decreasedValue; - [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDecrease); + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDecrease); } } @@ -441,7 +441,7 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { flutter::SemanticsAction action = GetSemanticsActionForScrollDirection(direction); if (![self node].HasAction(action)) return NO; - [self bridge] -> DispatchSemanticsAction([self uid], action); + [self bridge]->DispatchSemanticsAction([self uid], action); return YES; } @@ -450,7 +450,7 @@ - (BOOL)accessibilityPerformEscape { return NO; if (![self node].HasAction(flutter::SemanticsAction::kDismiss)) return NO; - [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDismiss); + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDismiss); return YES; } @@ -460,11 +460,11 @@ - (void)accessibilityElementDidBecomeFocused { if (![self isAccessibilityBridgeAlive]) return; if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden)) { - [self bridge] -> DispatchSemanticsAction([self uid], flutter::SemanticsAction::kShowOnScreen); + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kShowOnScreen); } if ([self node].HasAction(flutter::SemanticsAction::kDidGainAccessibilityFocus)) { - [self bridge] -> DispatchSemanticsAction([self uid], - flutter::SemanticsAction::kDidGainAccessibilityFocus); + [self bridge]->DispatchSemanticsAction([self uid], + flutter::SemanticsAction::kDidGainAccessibilityFocus); } } @@ -472,8 +472,8 @@ - (void)accessibilityElementDidLoseFocus { if (![self isAccessibilityBridgeAlive]) return; if ([self node].HasAction(flutter::SemanticsAction::kDidLoseAccessibilityFocus)) { - [self bridge] -> DispatchSemanticsAction([self uid], - flutter::SemanticsAction::kDidLoseAccessibilityFocus); + [self bridge]->DispatchSemanticsAction([self uid], + flutter::SemanticsAction::kDidLoseAccessibilityFocus); } } diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm index 9fb441413b665..1c299418928d2 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm @@ -200,7 +200,7 @@ - (void)setSemanticsNode:(const flutter::SemanticsNode*)node { if ([self node].HasFlag(flutter::SemanticsFlags::kIsFocused)) { // The text input view must have a non-trivial size for the accessibility // system to send text editing events. - [self bridge] -> textInputView().frame = CGRectMake(0.0, 0.0, 1.0, 1.0); + [self bridge]->textInputView().frame = CGRectMake(0.0, 0.0, 1.0, 1.0); } } @@ -216,7 +216,7 @@ - (void)setSemanticsNode:(const flutter::SemanticsNode*)node { */ - (UIView*)textInputSurrogate { if ([self node].HasFlag(flutter::SemanticsFlags::kIsFocused)) { - return [self bridge] -> textInputView(); + return [self bridge]->textInputView(); } else { return _inactive_text_input; } diff --git a/shell/platform/darwin/ios/platform_view_ios.mm b/shell/platform/darwin/ios/platform_view_ios.mm index 988ae8b3849c0..772d5775bab6f 100644 --- a/shell/platform/darwin/ios/platform_view_ios.mm +++ b/shell/platform/darwin/ios/platform_view_ios.mm @@ -185,7 +185,7 @@ new AccessibilityBridge(static_cast(owner_controller_.get().view), if (!owner_controller_) { return; } - [owner_controller_.get() platformViewsController] -> Reset(); + [owner_controller_.get() platformViewsController]->Reset(); } fml::scoped_nsprotocol PlatformViewIOS::GetTextInputPlugin() const { diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index 291d8cd266163..78286fcf54152 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -1430,7 +1430,7 @@ TEST_F(EmbedderTest, }); context.GetCompositor().SetPlatformViewRendererCallback( - [&](const FlutterLayer& layer, GrContext * + [&](const FlutterLayer& layer, GrContext* /* don't use because software compositor */) -> sk_sp { auto surface = CreateRenderSurface( layer, nullptr /* null because software compositor */); @@ -3001,6 +3001,12 @@ TEST_F(EmbedderTest, VerifyB143464703WithSoftwareBackend) { auto renderered_scene = context.GetNextSceneImage(); latch.Wait(); + + // TODO(https://github.com/flutter/flutter/issues/53784): enable this on all + // platforms. +#if !defined(OS_LINUX) + GTEST_SKIP() << "Skipping golden tests on non-Linux OSes"; +#endif // OS_LINUX ASSERT_TRUE(ImageMatchesFixture("verifyb143464703_soft_noxform.png", renderered_scene)); } diff --git a/shell/platform/fuchsia/flutter/vsync_waiter.cc b/shell/platform/fuchsia/flutter/vsync_waiter.cc index 283a33e14a6ad..6d2be8d0a0384 100644 --- a/shell/platform/fuchsia/flutter/vsync_waiter.cc +++ b/shell/platform/fuchsia/flutter/vsync_waiter.cc @@ -141,7 +141,7 @@ void VsyncWaiter::AwaitVSync() { fml::TimeDelta delta = next_vsync_start_time - now; task_runners_.GetUITaskRunner()->PostDelayedTask( - [& weak_factory_ui = this->weak_factory_ui_] { + [&weak_factory_ui = this->weak_factory_ui_] { if (!weak_factory_ui) { FML_LOG(WARNING) << "WeakPtrFactory for VsyncWaiter is null, likely " "due to the VsyncWaiter being destroyed."; diff --git a/shell/platform/fuchsia/flutter/vulkan_surface.cc b/shell/platform/fuchsia/flutter/vulkan_surface.cc index abbbe5d945742..781f9ae211c24 100644 --- a/shell/platform/fuchsia/flutter/vulkan_surface.cc +++ b/shell/platform/fuchsia/flutter/vulkan_surface.cc @@ -73,7 +73,7 @@ bool CreateVulkanImage(vulkan::VulkanProvider& vulkan_provider, } out_vulkan_image->vk_image = { - vk_image, [& vulkan_provider = vulkan_provider](VkImage image) { + vk_image, [&vulkan_provider = vulkan_provider](VkImage image) { vulkan_provider.vk().DestroyImage(vulkan_provider.vk_device(), image, NULL); }}; @@ -263,8 +263,8 @@ bool VulkanSurface::AllocateDeviceMemory(sk_sp context, return false; } - vk_memory_ = {vk_memory, [& vulkan_provider = - vulkan_provider_](VkDeviceMemory memory) { + vk_memory_ = {vk_memory, + [&vulkan_provider = vulkan_provider_](VkDeviceMemory memory) { vulkan_provider.vk().FreeMemory(vulkan_provider.vk_device(), memory, NULL); }}; diff --git a/sky/tools/create_ios_framework.py b/sky/tools/create_ios_framework.py index bb74044b546e6..8c79921c589f5 100755 --- a/sky/tools/create_ios_framework.py +++ b/sky/tools/create_ios_framework.py @@ -10,6 +10,9 @@ import os +DSYMUTIL = os.path.join(os.path.dirname(__file__), '..', '..', '..', + 'buildtools', 'mac-x64', 'clang', 'bin', 'dsymutil') + def main(): parser = argparse.ArgumentParser(description='Creates Flutter.framework') @@ -33,27 +36,31 @@ def main(): simulator_dylib = os.path.join(simulator_framework, 'Flutter') if not os.path.isdir(arm64_framework): - print 'Cannot find iOS arm64 Framework at', arm64_framework + print('Cannot find iOS arm64 Framework at %s' % arm64_framework) return 1 if not os.path.isdir(armv7_framework): - print 'Cannot find iOS armv7 Framework at', armv7_framework + print('Cannot find iOS armv7 Framework at %s' % armv7_framework) return 1 if not os.path.isdir(simulator_framework): - print 'Cannot find iOS simulator Framework at', simulator_framework + print('Cannot find iOS simulator Framework at %s' % simulator_framework) return 1 if not os.path.isfile(arm64_dylib): - print 'Cannot find iOS arm64 dylib at', arm64_dylib + print('Cannot find iOS arm64 dylib at %s' % arm64_dylib) return 1 if not os.path.isfile(armv7_dylib): - print 'Cannot find iOS armv7 dylib at', armv7_dylib + print('Cannot find iOS armv7 dylib at %s' % armv7_dylib) return 1 if not os.path.isfile(simulator_dylib): - print 'Cannot find iOS simulator dylib at', simulator_dylib + print('Cannot find iOS simulator dylib at %s' % simulator_dylib) + return 1 + + if not os.path.isfile(DSYMUTIL): + print('Cannot find dsymutil at %s' % DSYMUTIL) return 1 shutil.rmtree(fat_framework, True) @@ -76,7 +83,7 @@ def main(): if args.dsym: dsym_out = os.path.splitext(fat_framework)[0] + '.dSYM' - subprocess.check_call(['dsymutil', '-o', dsym_out, linker_out]) + subprocess.check_call([DSYMUTIL, '-o', dsym_out, linker_out]) if args.strip: # copy unstripped diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 85fbe373e5f3d..25d1ed04dc4d6 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -181,7 +181,7 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_gradient.png'); expect(areEqual, true); - }); + }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 test('Simple dithered gradient', () async { Paint.enableDithering = true; @@ -197,5 +197,5 @@ void main() { final bool areEqual = await fuzzyGoldenImageCompare(image, 'canvas_test_dithered_gradient.png'); expect(areEqual, true); - }); + }, skip: !Platform.isLinux); // https://github.com/flutter/flutter/issues/53784 } diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 2b822d08fea79..2bd2d236d0e49 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -110,8 +110,93 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { 'JNI_OnLoad': 'T', '_binary_icudtl_dat_size': 'A', '_binary_icudtl_dat_start': 'D', + // TODO(fxb/47943): Remove these once Clang lld does not expose them. + // arm + '__adddf3': 'T', + '__addsf3': 'T', + '__aeabi_cdcmpeq': 'T', + '__aeabi_cdcmple': 'T', + '__aeabi_cdrcmple': 'T', + '__aeabi_d2lz': 'T', + '__aeabi_d2uiz': 'T', + '__aeabi_d2ulz': 'T', + '__aeabi_dadd': 'T', + '__aeabi_dcmpeq': 'T', + '__aeabi_dcmpge': 'T', + '__aeabi_dcmpgt': 'T', + '__aeabi_dcmple': 'T', + '__aeabi_dcmplt': 'T', + '__aeabi_ddiv': 'T', + '__aeabi_dmul': 'T', + '__aeabi_drsub': 'T', + '__aeabi_dsub': 'T', + '__aeabi_f2d': 'T', + '__aeabi_fadd': 'T', + '__aeabi_frsub': 'T', + '__aeabi_fsub': 'T', + '__aeabi_i2d': 'T', + '__aeabi_i2f': 'T', + '__aeabi_l2d': 'T', + '__aeabi_l2f': 'T', + '__aeabi_lasr': 'T', + '__aeabi_ldivmod': 'T', + '__aeabi_llsl': 'T', + '__aeabi_llsr': 'T', + '__aeabi_ui2d': 'T', + '__aeabi_ui2f': 'T', + '__aeabi_uidiv': 'T', + '__aeabi_uidivmod': 'T', + '__aeabi_ul2d': 'T', + '__aeabi_ul2f': 'T', + '__aeabi_uldivmod': 'T', + '__ashldi3': 'T', + '__ashrdi3': 'T', + '__cmpdf2': 'T', + '__divdf3': 'T', + '__divdi3': 'T', + '__eqdf2': 'T', + '__extendsfdf2': 'T', + '__fixdfdi': 'T', + '__fixunsdfdi': 'T', + '__fixunsdfsi': 'T', + '__floatdidf': 'T', + '__floatdisf': 'T', + '__floatsidf': 'T', + '__floatsisf': 'T', + '__floatundidf': 'T', + '__floatundisf': 'T', + '__floatunsidf': 'T', + '__floatunsisf': 'T', + '__gedf2': 'T', + '__gnu_ldivmod_helper': 'T', + '__gnu_uldivmod_helper': 'T', + '__gtdf2': 'T', + '__ledf2': 'T', + '__lshrdi3': 'T', + '__ltdf2': 'T', + '__muldf3': 'T', + '__nedf2': 'T', + '__subdf3': 'T', + '__subsf3': 'T', + '__udivdi3': 'T', + '__udivsi3': 'T', + // arm64 + '__clz_tab': 'R', + '__udivti3': 'T', + // arm64 && x64 + '__emutls_get_address': 'T', + '__emutls_register_common': 'T', + // jit x86 + '__moddi3': 'T', + '__umoddi3': 'T', }; - if (!const MapEquality().equals(entryMap, expectedSymbols)) { + final Map badSymbols = {}; + for (final String key in entryMap.keys) { + if (entryMap[key] != expectedSymbols[key]) { + badSymbols[key] = entryMap[key]; + } + } + if (badSymbols.isNotEmpty) { print('ERROR: $libFlutter exports the wrong symbols'); print(' Expected $expectedSymbols'); print(' Library has $entryMap.'); diff --git a/third_party/txt/src/minikin/HbFontCache.cpp b/third_party/txt/src/minikin/HbFontCache.cpp index 5fc2f59633a18..d60fcc73730e0 100644 --- a/third_party/txt/src/minikin/HbFontCache.cpp +++ b/third_party/txt/src/minikin/HbFontCache.cpp @@ -105,7 +105,7 @@ hb_font_t* getHbFontLocked(const MinikinFont* minikinFont) { font = hb_font_create_sub_font(parent_font); std::vector variations; for (const FontVariation& variation : minikinFont->GetAxes()) { - variations.push_back({variation.axisTag, variation.value}); + variations.push_back({variation.axisTag, variation.value}); } hb_font_set_variations(font, variations.data(), variations.size()); hb_font_destroy(parent_font); From 08ae3bb63e097a34414c41601de3a9fddc5fb3f5 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 2 Apr 2020 14:57:46 -0700 Subject: [PATCH 247/386] Remove JSON codec from C++ client wrapper (#17312) The JSON codec is awkward to use in the wrapper (since the client has to build and link one of the JSON libraries to do so). Since it would be very cumbersome to wrap in a C API, and there's essentially no reason to use it instead of the standard codec, this removes it from the wrapper entirely. Since some system channels (internal to the engine) still use it, it's moved into common/cpp instead of being eliminated entirely. Internally we always use RapidJSON though, so the jsoncpp implementation is removed. Also adds some unit test coverage, since there wasn't any. Fixes #30669 --- BUILD.gn | 1 + ci/licenses_golden/licenses_flutter | 11 +- shell/platform/common/cpp/BUILD.gn | 34 +++++- .../common/cpp/client_wrapper/BUILD.gn | 3 - .../cpp/client_wrapper/core_wrapper_files.gni | 23 ++-- .../include/flutter/json_type.h | 26 ----- .../json_message_codec.cc | 34 ++---- .../include/flutter => }/json_message_codec.h | 17 +-- .../cpp/json_message_codec_unittests.cc | 46 ++++++++ .../{client_wrapper => }/json_method_codec.cc | 43 ++----- .../include/flutter => }/json_method_codec.h | 23 ++-- .../common/cpp/json_method_codec_unittests.cc | 107 ++++++++++++++++++ shell/platform/glfw/BUILD.gn | 2 - shell/platform/glfw/key_event_handler.cc | 2 +- shell/platform/glfw/platform_handler.cc | 2 +- shell/platform/glfw/text_input_plugin.cc | 2 +- shell/platform/windows/BUILD.gn | 2 - shell/platform/windows/key_event_handler.cc | 2 +- shell/platform/windows/platform_handler.cc | 2 +- shell/platform/windows/text_input_plugin.cc | 2 +- testing/run_tests.py | 2 + 21 files changed, 239 insertions(+), 147 deletions(-) delete mode 100644 shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h rename shell/platform/common/cpp/{client_wrapper => }/json_message_codec.cc (53%) rename shell/platform/common/cpp/{client_wrapper/include/flutter => }/json_message_codec.h (64%) create mode 100644 shell/platform/common/cpp/json_message_codec_unittests.cc rename shell/platform/common/cpp/{client_wrapper => }/json_method_codec.cc (75%) rename shell/platform/common/cpp/{client_wrapper/include/flutter => }/json_method_codec.h (60%) create mode 100644 shell/platform/common/cpp/json_method_codec_unittests.cc diff --git a/BUILD.gn b/BUILD.gn index d35c608ba1d63..fea2a2b5bebf1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -76,6 +76,7 @@ group("flutter") { "//flutter/runtime:runtime_unittests", "//flutter/shell/common:shell_unittests", "//flutter/shell/platform/common/cpp:common_cpp_core_unittests", + "//flutter/shell/platform/common/cpp:common_cpp_unittests", "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper_unittests", "//flutter/shell/platform/embedder:embedder_unittests", "//flutter/shell/platform/glfw/client_wrapper:client_wrapper_glfw_unittests", diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 903a33b252e51..724af2afdc5e0 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -778,9 +778,6 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/binary_messenger.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/encodable_value.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/engine_method_result.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/message_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_channel.h @@ -790,8 +787,6 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registry.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_message_codec.h FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/include/flutter/standard_method_codec.h -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/json_message_codec.cc -FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/json_method_codec.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_call_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/method_channel_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc @@ -802,6 +797,12 @@ FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_message FILE: ../../../flutter/shell/platform/common/cpp/client_wrapper/standard_method_codec_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.cc FILE: ../../../flutter/shell/platform/common/cpp/incoming_message_dispatcher.h +FILE: ../../../flutter/shell/platform/common/cpp/json_message_codec.cc +FILE: ../../../flutter/shell/platform/common/cpp/json_message_codec.h +FILE: ../../../flutter/shell/platform/common/cpp/json_message_codec_unittests.cc +FILE: ../../../flutter/shell/platform/common/cpp/json_method_codec.cc +FILE: ../../../flutter/shell/platform/common/cpp/json_method_codec.h +FILE: ../../../flutter/shell/platform/common/cpp/json_method_codec_unittests.cc FILE: ../../../flutter/shell/platform/common/cpp/path_utils.cc FILE: ../../../flutter/shell/platform/common/cpp/path_utils.h FILE: ../../../flutter/shell/platform/common/cpp/path_utils_unittests.cc diff --git a/shell/platform/common/cpp/BUILD.gn b/shell/platform/common/cpp/BUILD.gn index 00db206ed2a70..4d211c8fe8de1 100644 --- a/shell/platform/common/cpp/BUILD.gn +++ b/shell/platform/common/cpp/BUILD.gn @@ -33,6 +33,8 @@ source_set("common_cpp_library_headers") { source_set("common_cpp") { public = [ "incoming_message_dispatcher.h", + "json_message_codec.h", + "json_method_codec.h", "text_input_model.h", ] @@ -40,6 +42,8 @@ source_set("common_cpp") { # to the _public_headers above into this target. sources = [ "incoming_message_dispatcher.cc", + "json_message_codec.cc", + "json_method_codec.cc", "text_input_model.cc", ] @@ -53,16 +57,12 @@ source_set("common_cpp") { public_deps = [ ":common_cpp_core", + "//third_party/rapidjson", ] - # TODO: Remove once text input model refactor lands, at which point this code - # won't have a JSON dependency. - defines = [ "USE_RAPID_JSON" ] - deps += [ "//third_party/rapidjson" ] - if (is_win) { # For wstring_conversion. See issue #50053. - defines += [ "_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING" ] + defines = [ "_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING" ] } } @@ -101,6 +101,28 @@ executable("common_cpp_core_unittests") { public_configs = [ "//flutter:config" ] } +test_fixtures("common_cpp_fixtures") { + fixtures = [] +} + +executable("common_cpp_unittests") { + testonly = true + + sources = [ + "json_message_codec_unittests.cc", + "json_method_codec_unittests.cc", + ] + + deps = [ + ":common_cpp", + ":common_cpp_fixtures", + "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper_library_stubs", + "//flutter/testing", + ] + + public_configs = [ "//flutter:config" ] +} + copy("publish_headers") { sources = _public_headers outputs = [ diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index a8d30109d2856..f46cd81461ed0 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -12,11 +12,8 @@ source_set("client_wrapper") { deps = [ "//flutter/shell/platform/common/cpp:common_cpp_library_headers", - "//third_party/rapidjson", ] - defines = [ "USE_RAPID_JSON" ] - configs += [ "//flutter/shell/platform/common/cpp:desktop_library_implementation" ] diff --git a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni index ce91be7866717..0662cca6be422 100644 --- a/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni +++ b/shell/platform/common/cpp/client_wrapper/core_wrapper_files.gni @@ -8,9 +8,6 @@ core_cpp_client_wrapper_includes = "include/flutter/binary_messenger.h", "include/flutter/encodable_value.h", "include/flutter/engine_method_result.h", - "include/flutter/json_message_codec.h", - "include/flutter/json_method_codec.h", - "include/flutter/json_type.h", "include/flutter/message_codec.h", "include/flutter/method_call.h", "include/flutter/method_channel.h", @@ -26,15 +23,11 @@ core_cpp_client_wrapper_includes = # TODO: Once the wrapper API is more stable, consolidate to as few files as is # reasonable (without forcing different kinds of clients to take unnecessary # code) to simplify use. -core_cpp_client_wrapper_sources = - get_path_info( - [ - "byte_stream_wrappers.h", - "engine_method_result.cc", - "json_message_codec.cc", # TODO combine into a single json_codec.cc. - "json_method_codec.cc", # TODO combine into a single json_codec.cc. - "plugin_registrar.cc", - "standard_codec_serializer.h", - "standard_codec.cc", - ], - "abspath") +core_cpp_client_wrapper_sources = get_path_info([ + "byte_stream_wrappers.h", + "engine_method_result.cc", + "plugin_registrar.cc", + "standard_codec_serializer.h", + "standard_codec.cc", + ], + "abspath") diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h b/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h deleted file mode 100644 index 24baf1328d668..0000000000000 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/json_type.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ -#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ - -// By default, the Json codecs use jsoncpp, but a version using RapidJSON is -// implemented as well. To use the latter, set USE_RAPID_JSON. -// -// When writing code using the JSON codec classes, do not use JsonValueType; -// instead use the underlying type for the library you have selected directly. - -#ifdef USE_RAPID_JSON -#include - -// The APIs often pass owning references, which in RapidJSON must include the -// allocator, so the value type for the APIs is Document rather than Value. -using JsonValueType = rapidjson::Document; -#else -#include - -using JsonValueType = Json::Value; -#endif - -#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_TYPE_H_ diff --git a/shell/platform/common/cpp/client_wrapper/json_message_codec.cc b/shell/platform/common/cpp/json_message_codec.cc similarity index 53% rename from shell/platform/common/cpp/client_wrapper/json_message_codec.cc rename to shell/platform/common/cpp/json_message_codec.cc index 5610c4762ea83..d2a43bd806ded 100644 --- a/shell/platform/common/cpp/client_wrapper/json_message_codec.cc +++ b/shell/platform/common/cpp/json_message_codec.cc @@ -2,16 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "include/flutter/json_message_codec.h" +#include "flutter/shell/platform/common/cpp/json_message_codec.h" #include #include -#ifdef USE_RAPID_JSON #include "rapidjson/error/en.h" #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" -#endif namespace flutter { @@ -22,8 +20,7 @@ const JsonMessageCodec& JsonMessageCodec::GetInstance() { } std::unique_ptr> JsonMessageCodec::EncodeMessageInternal( - const JsonValueType& message) const { -#ifdef USE_RAPID_JSON + const rapidjson::Document& message) const { // TODO: Look into alternate writers that would avoid the buffer copy. rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -31,37 +28,20 @@ std::unique_ptr> JsonMessageCodec::EncodeMessageInternal( const char* buffer_start = buffer.GetString(); return std::make_unique>( buffer_start, buffer_start + buffer.GetSize()); -#else - Json::StreamWriterBuilder writer_builder; - std::string serialization = Json::writeString(writer_builder, message); - return std::make_unique>(serialization.begin(), - serialization.end()); -#endif } -std::unique_ptr JsonMessageCodec::DecodeMessageInternal( +std::unique_ptr JsonMessageCodec::DecodeMessageInternal( const uint8_t* binary_message, const size_t message_size) const { auto raw_message = reinterpret_cast(binary_message); - auto json_message = std::make_unique(); - std::string parse_errors; - bool parsing_successful = false; -#ifdef USE_RAPID_JSON + auto json_message = std::make_unique(); rapidjson::ParseResult result = json_message->Parse(raw_message, message_size); - parsing_successful = result == rapidjson::ParseErrorCode::kParseErrorNone; - if (!parsing_successful) { - parse_errors = rapidjson::GetParseError_En(result.Code()); - } -#else - Json::CharReaderBuilder reader_builder; - std::unique_ptr parser(reader_builder.newCharReader()); - parsing_successful = parser->parse(raw_message, raw_message + message_size, - json_message.get(), &parse_errors); -#endif + bool parsing_successful = + result == rapidjson::ParseErrorCode::kParseErrorNone; if (!parsing_successful) { std::cerr << "Unable to parse JSON message:" << std::endl - << parse_errors << std::endl; + << rapidjson::GetParseError_En(result.Code()) << std::endl; return nullptr; } return json_message; diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h b/shell/platform/common/cpp/json_message_codec.h similarity index 64% rename from shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h rename to shell/platform/common/cpp/json_message_codec.h index ba1aa2ffc3520..81f2502c0c71d 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h +++ b/shell/platform/common/cpp/json_message_codec.h @@ -2,17 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ -#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_JSON_MESSAGE_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_JSON_MESSAGE_CODEC_H_ -#include "json_type.h" -#include "message_codec.h" +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/message_codec.h" namespace flutter { // A message encoding/decoding mechanism for communications to/from the // Flutter engine via JSON channels. -class JsonMessageCodec : public MessageCodec { +class JsonMessageCodec : public MessageCodec { public: // Returns the shared instance of the codec. static const JsonMessageCodec& GetInstance(); @@ -28,15 +29,15 @@ class JsonMessageCodec : public MessageCodec { JsonMessageCodec() = default; // |flutter::MessageCodec| - std::unique_ptr DecodeMessageInternal( + std::unique_ptr DecodeMessageInternal( const uint8_t* binary_message, const size_t message_size) const override; // |flutter::MessageCodec| std::unique_ptr> EncodeMessageInternal( - const JsonValueType& message) const override; + const rapidjson::Document& message) const override; }; } // namespace flutter -#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_MESSAGE_CODEC_H_ +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_JSON_MESSAGE_CODEC_H_ diff --git a/shell/platform/common/cpp/json_message_codec_unittests.cc b/shell/platform/common/cpp/json_message_codec_unittests.cc new file mode 100644 index 0000000000000..3354f30a728df --- /dev/null +++ b/shell/platform/common/cpp/json_message_codec_unittests.cc @@ -0,0 +1,46 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/common/cpp/json_message_codec.h" + +#include +#include +#include + +#include "gtest/gtest.h" + +namespace flutter { + +namespace { + +// Validates round-trip encoding and decoding of |value|. +static void CheckEncodeDecode(const rapidjson::Document& value) { + const JsonMessageCodec& codec = JsonMessageCodec::GetInstance(); + auto encoded = codec.EncodeMessage(value); + ASSERT_TRUE(encoded); + auto decoded = codec.DecodeMessage(*encoded); + EXPECT_EQ(value, *decoded); +} + +} // namespace + +// Tests that a JSON document with various data types round-trips correctly. +TEST(JsonMessageCodec, EncodeDecode) { + rapidjson::Document array(rapidjson::kArrayType); + auto& allocator = array.GetAllocator(); + + array.PushBack("string", allocator); + + rapidjson::Value map(rapidjson::kObjectType); + map.AddMember("a", -7, allocator); + map.AddMember("b", std::numeric_limits::max(), allocator); + map.AddMember("c", 3.14159, allocator); + map.AddMember("d", true, allocator); + map.AddMember("e", rapidjson::Value(), allocator); + array.PushBack(map, allocator); + + CheckEncodeDecode(array); +} + +} // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/json_method_codec.cc b/shell/platform/common/cpp/json_method_codec.cc similarity index 75% rename from shell/platform/common/cpp/client_wrapper/json_method_codec.cc rename to shell/platform/common/cpp/json_method_codec.cc index 6c9528ff2e304..26f6a2402082b 100644 --- a/shell/platform/common/cpp/client_wrapper/json_method_codec.cc +++ b/shell/platform/common/cpp/json_method_codec.cc @@ -2,9 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "include/flutter/json_method_codec.h" +#include "flutter/shell/platform/common/cpp/json_method_codec.h" -#include "include/flutter/json_message_codec.h" +#include "flutter/shell/platform/common/cpp/json_message_codec.h" namespace flutter { @@ -20,16 +20,15 @@ const JsonMethodCodec& JsonMethodCodec::GetInstance() { return sInstance; } -std::unique_ptr> +std::unique_ptr> JsonMethodCodec::DecodeMethodCallInternal(const uint8_t* message, const size_t message_size) const { - std::unique_ptr json_message = + std::unique_ptr json_message = JsonMessageCodec::GetInstance().DecodeMessage(message, message_size); if (!json_message) { return nullptr; } -#if USE_RAPID_JSON auto method_name_iter = json_message->FindMember(kMessageMethodKey); if (method_name_iter == json_message->MemberEnd()) { return nullptr; @@ -57,20 +56,10 @@ JsonMethodCodec::DecodeMethodCallInternal(const uint8_t* message, } return std::make_unique>( method_name, std::move(arguments)); -#else - Json::Value method = (*json_message)[kMessageMethodKey]; - if (method.isNull()) { - return nullptr; - } - return std::make_unique>( - method.asString(), - std::make_unique((*json_message)[kMessageArgumentsKey])); -#endif } std::unique_ptr> JsonMethodCodec::EncodeMethodCallInternal( - const MethodCall& method_call) const { -#if USE_RAPID_JSON + const MethodCall& method_call) const { // TODO: Consider revisiting the codec APIs to avoid the need to copy // everything when doing encoding (e.g., by having a version that takes // owership of the object to encode, so that it can be moved instead). @@ -83,20 +72,13 @@ std::unique_ptr> JsonMethodCodec::EncodeMethodCallInternal( } message.AddMember(kMessageMethodKey, name, allocator); message.AddMember(kMessageArgumentsKey, arguments, allocator); -#else - Json::Value message(Json::objectValue); - message[kMessageMethodKey] = method_call.method_name(); - const Json::Value* arguments = method_call.arguments(); - message[kMessageArgumentsKey] = arguments ? *arguments : Json::Value(); -#endif return JsonMessageCodec::GetInstance().EncodeMessage(message); } std::unique_ptr> JsonMethodCodec::EncodeSuccessEnvelopeInternal( - const JsonValueType* result) const { -#if USE_RAPID_JSON + const rapidjson::Document* result) const { rapidjson::Document envelope; envelope.SetArray(); rapidjson::Value result_value; @@ -104,10 +86,6 @@ JsonMethodCodec::EncodeSuccessEnvelopeInternal( result_value.CopyFrom(*result, envelope.GetAllocator()); } envelope.PushBack(result_value, envelope.GetAllocator()); -#else - Json::Value envelope(Json::arrayValue); - envelope.append(result == nullptr ? Json::Value() : *result); -#endif return JsonMessageCodec::GetInstance().EncodeMessage(envelope); } @@ -116,8 +94,7 @@ std::unique_ptr> JsonMethodCodec::EncodeErrorEnvelopeInternal( const std::string& error_code, const std::string& error_message, - const JsonValueType* error_details) const { -#if USE_RAPID_JSON + const rapidjson::Document* error_details) const { rapidjson::Document envelope(rapidjson::kArrayType); auto& allocator = envelope.GetAllocator(); envelope.PushBack(rapidjson::Value(error_code, allocator), allocator); @@ -127,12 +104,6 @@ JsonMethodCodec::EncodeErrorEnvelopeInternal( details_value.CopyFrom(*error_details, allocator); } envelope.PushBack(details_value, allocator); -#else - Json::Value envelope(Json::arrayValue); - envelope.append(error_code); - envelope.append(error_message.empty() ? Json::Value() : error_message); - envelope.append(error_details == nullptr ? Json::Value() : *error_details); -#endif return JsonMessageCodec::GetInstance().EncodeMessage(envelope); } diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h b/shell/platform/common/cpp/json_method_codec.h similarity index 60% rename from shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h rename to shell/platform/common/cpp/json_method_codec.h index 92af82000ac96..597fb6e6d1f16 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h +++ b/shell/platform/common/cpp/json_method_codec.h @@ -2,17 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ -#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ +#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_JSON_METHOD_CODEC_H_ +#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_JSON_METHOD_CODEC_H_ -#include "json_type.h" -#include "method_call.h" -#include "method_codec.h" +#include + +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_call.h" +#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/method_codec.h" namespace flutter { // An implementation of MethodCodec that uses JSON strings as the serialization. -class JsonMethodCodec : public MethodCodec { +class JsonMethodCodec : public MethodCodec { public: // Returns the shared instance of the codec. static const JsonMethodCodec& GetInstance(); @@ -28,25 +29,25 @@ class JsonMethodCodec : public MethodCodec { JsonMethodCodec() = default; // |flutter::MethodCodec| - std::unique_ptr> DecodeMethodCallInternal( + std::unique_ptr> DecodeMethodCallInternal( const uint8_t* message, const size_t message_size) const override; // |flutter::MethodCodec| std::unique_ptr> EncodeMethodCallInternal( - const MethodCall& method_call) const override; + const MethodCall& method_call) const override; // |flutter::MethodCodec| std::unique_ptr> EncodeSuccessEnvelopeInternal( - const JsonValueType* result) const override; + const rapidjson::Document* result) const override; // |flutter::MethodCodec| std::unique_ptr> EncodeErrorEnvelopeInternal( const std::string& error_code, const std::string& error_message, - const JsonValueType* error_details) const override; + const rapidjson::Document* error_details) const override; }; } // namespace flutter -#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_JSON_METHOD_CODEC_H_ +#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_JSON_METHOD_CODEC_H_ diff --git a/shell/platform/common/cpp/json_method_codec_unittests.cc b/shell/platform/common/cpp/json_method_codec_unittests.cc new file mode 100644 index 0000000000000..36bffcff1d104 --- /dev/null +++ b/shell/platform/common/cpp/json_method_codec_unittests.cc @@ -0,0 +1,107 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/common/cpp/json_method_codec.h" + +#include "gtest/gtest.h" + +namespace flutter { + +namespace { + +// Returns true if the given method calls have the same method name, and their +// arguments have equivalent values. +bool MethodCallsAreEqual(const MethodCall& a, + const MethodCall& b) { + if (a.method_name() != b.method_name()) { + return false; + } + // Treat nullptr and Null as equivalent. + if ((!a.arguments() || a.arguments()->IsNull()) && + (!b.arguments() || b.arguments()->IsNull())) { + return true; + } + return *a.arguments() == *b.arguments(); +} + +} // namespace + +TEST(JsonMethodCodec, HandlesMethodCallsWithNullArguments) { + const JsonMethodCodec& codec = JsonMethodCodec::GetInstance(); + MethodCall call("hello", nullptr); + auto encoded = codec.EncodeMethodCall(call); + ASSERT_TRUE(encoded); + std::unique_ptr> decoded = + codec.DecodeMethodCall(*encoded); + ASSERT_TRUE(decoded); + EXPECT_TRUE(MethodCallsAreEqual(call, *decoded)); +} + +TEST(JsonMethodCodec, HandlesMethodCallsWithArgument) { + const JsonMethodCodec& codec = JsonMethodCodec::GetInstance(); + + auto arguments = std::make_unique(rapidjson::kArrayType); + auto& allocator = arguments->GetAllocator(); + arguments->PushBack(42, allocator); + arguments->PushBack("world", allocator); + MethodCall call("hello", std::move(arguments)); + auto encoded = codec.EncodeMethodCall(call); + ASSERT_TRUE(encoded); + std::unique_ptr> decoded = + codec.DecodeMethodCall(*encoded); + ASSERT_TRUE(decoded); + EXPECT_TRUE(MethodCallsAreEqual(call, *decoded)); +} + +TEST(JsonMethodCodec, HandlesSuccessEnvelopesWithNullResult) { + const JsonMethodCodec& codec = JsonMethodCodec::GetInstance(); + auto encoded = codec.EncodeSuccessEnvelope(); + ASSERT_TRUE(encoded); + std::vector bytes = {'[', 'n', 'u', 'l', 'l', ']'}; + EXPECT_EQ(*encoded, bytes); + // TODO: Add round-trip check once decoding replies is implemented. +} + +TEST(JsonMethodCodec, HandlesSuccessEnvelopesWithResult) { + const JsonMethodCodec& codec = JsonMethodCodec::GetInstance(); + rapidjson::Document result; + result.SetInt(42); + auto encoded = codec.EncodeSuccessEnvelope(&result); + ASSERT_TRUE(encoded); + std::vector bytes = {'[', '4', '2', ']'}; + EXPECT_EQ(*encoded, bytes); + // TODO: Add round-trip check once decoding replies is implemented. +} + +TEST(JsonMethodCodec, HandlesErrorEnvelopesWithNulls) { + const JsonMethodCodec& codec = JsonMethodCodec::GetInstance(); + auto encoded = codec.EncodeErrorEnvelope("errorCode"); + ASSERT_TRUE(encoded); + std::vector bytes = { + '[', '"', 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e', + '"', ',', '"', '"', ',', 'n', 'u', 'l', 'l', ']', + }; + EXPECT_EQ(*encoded, bytes); + // TODO: Add round-trip check once decoding replies is implemented. +} + +TEST(JsonMethodCodec, HandlesErrorEnvelopesWithDetails) { + const JsonMethodCodec& codec = JsonMethodCodec::GetInstance(); + rapidjson::Document details(rapidjson::kArrayType); + auto& allocator = details.GetAllocator(); + details.PushBack("a", allocator); + details.PushBack(42, allocator); + auto encoded = + codec.EncodeErrorEnvelope("errorCode", "something failed", &details); + ASSERT_NE(encoded.get(), nullptr); + std::vector bytes = { + '[', '"', 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e', '"', ',', '"', + 's', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', ' ', 'f', 'a', 'i', 'l', + 'e', 'd', '"', ',', '[', '"', 'a', '"', ',', '4', '2', ']', ']', + }; + EXPECT_EQ(*encoded, bytes); + // TODO: Add round-trip check once decoding replies is implemented. +} + +} // namespace flutter diff --git a/shell/platform/glfw/BUILD.gn b/shell/platform/glfw/BUILD.gn index 546718bc14158..c78eef6183690 100644 --- a/shell/platform/glfw/BUILD.gn +++ b/shell/platform/glfw/BUILD.gn @@ -42,8 +42,6 @@ source_set("flutter_glfw") { "text_input_plugin.h", ] - defines = [ "USE_RAPID_JSON" ] - configs += [ "//flutter/shell/platform/common/cpp:desktop_library_implementation" ] diff --git a/shell/platform/glfw/key_event_handler.cc b/shell/platform/glfw/key_event_handler.cc index 2ad013c064462..bdd121091f54e 100644 --- a/shell/platform/glfw/key_event_handler.cc +++ b/shell/platform/glfw/key_event_handler.cc @@ -6,7 +6,7 @@ #include -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h" +#include "flutter/shell/platform/common/cpp/json_message_codec.h" static constexpr char kChannelName[] = "flutter/keyevent"; diff --git a/shell/platform/glfw/platform_handler.cc b/shell/platform/glfw/platform_handler.cc index 39c8b4708cd75..6d4046364abc6 100644 --- a/shell/platform/glfw/platform_handler.cc +++ b/shell/platform/glfw/platform_handler.cc @@ -6,7 +6,7 @@ #include -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h" +#include "flutter/shell/platform/common/cpp/json_method_codec.h" static constexpr char kChannelName[] = "flutter/platform"; diff --git a/shell/platform/glfw/text_input_plugin.cc b/shell/platform/glfw/text_input_plugin.cc index 94fa0a267cf5e..d1f163b2f1065 100644 --- a/shell/platform/glfw/text_input_plugin.cc +++ b/shell/platform/glfw/text_input_plugin.cc @@ -7,7 +7,7 @@ #include #include -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h" +#include "flutter/shell/platform/common/cpp/json_method_codec.h" static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState"; static constexpr char kClearClientMethod[] = "TextInput.clearClient"; diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index fee227ea7c9e0..88869ace762eb 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -60,8 +60,6 @@ source_set("flutter_windows_source") { "window_state.h", ] - defines = [ "USE_RAPID_JSON" ] - configs += [ "//flutter/shell/platform/common/cpp:desktop_library_implementation", "//third_party/angle:gl_prototypes", diff --git a/shell/platform/windows/key_event_handler.cc b/shell/platform/windows/key_event_handler.cc index 63af2446b889b..f611210174b75 100644 --- a/shell/platform/windows/key_event_handler.cc +++ b/shell/platform/windows/key_event_handler.cc @@ -8,7 +8,7 @@ #include -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_message_codec.h" +#include "flutter/shell/platform/common/cpp/json_message_codec.h" static constexpr char kChannelName[] = "flutter/keyevent"; diff --git a/shell/platform/windows/platform_handler.cc b/shell/platform/windows/platform_handler.cc index c4bf592744345..f1540a9819016 100644 --- a/shell/platform/windows/platform_handler.cc +++ b/shell/platform/windows/platform_handler.cc @@ -8,7 +8,7 @@ #include -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h" +#include "flutter/shell/platform/common/cpp/json_method_codec.h" static constexpr char kChannelName[] = "flutter/platform"; diff --git a/shell/platform/windows/text_input_plugin.cc b/shell/platform/windows/text_input_plugin.cc index 6b71ff23e5c3d..0fd4dee9d2442 100644 --- a/shell/platform/windows/text_input_plugin.cc +++ b/shell/platform/windows/text_input_plugin.cc @@ -9,7 +9,7 @@ #include #include -#include "flutter/shell/platform/common/cpp/client_wrapper/include/flutter/json_method_codec.h" +#include "flutter/shell/platform/common/cpp/json_method_codec.h" static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState"; static constexpr char kClearClientMethod[] = "TextInput.clearClient"; diff --git a/testing/run_tests.py b/testing/run_tests.py index 68abca30b37de..3fe88f065f4ce 100755 --- a/testing/run_tests.py +++ b/testing/run_tests.py @@ -110,6 +110,8 @@ def RunCCTests(build_dir, filter): RunEngineExecutable(build_dir, 'common_cpp_core_unittests', filter, shuffle_flags) + RunEngineExecutable(build_dir, 'common_cpp_unittests', filter, shuffle_flags) + RunEngineExecutable(build_dir, 'client_wrapper_unittests', filter, shuffle_flags) # https://github.com/flutter/flutter/issues/36294 From 2e909652ac7f3f848dc29cb6487a3ba8da4d6b46 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 2 Apr 2020 15:06:03 -0700 Subject: [PATCH 248/386] Fix bad texture view config (#17486) --- .../io/flutter/embedding/android/FlutterView.java | 2 +- .../android/test/io/flutter/FlutterTestSuite.java | 3 ++- .../FlutterActivityAndFragmentDelegateTest.java | 11 +++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 5d782b678ff6f..31cbeb9673484 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -264,7 +264,7 @@ private FlutterView( super(context, attrs); this.flutterTextureView = flutterTextureView; - this.renderSurface = flutterSurfaceView; + this.renderSurface = flutterTextureView; init(); } diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index 480998ac362d4..daf060a2c3b9f 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -4,6 +4,7 @@ package io.flutter; +import io.flutter.embedding.android.FlutterActivityAndFragmentDelegateTest; import io.flutter.embedding.android.FlutterActivityTest; import io.flutter.embedding.android.FlutterAndroidComponentTest; import io.flutter.embedding.android.FlutterFragmentTest; @@ -32,8 +33,8 @@ @RunWith(Suite.class) @SuiteClasses({ - // FlutterActivityAndFragmentDelegateTest.class, //TODO(mklim): Fix and re-enable this DartExecutorTest.class, + FlutterActivityAndFragmentDelegateTest.class, FlutterActivityTest.class, FlutterAndroidComponentTest.class, FlutterEngineCacheTest.class, diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index 3d8f420795f22..71a946f8a8902 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -220,7 +220,18 @@ public void itGivesHostAnOpportunityToConfigureFlutterSurfaceView() { public void itGivesHostAnOpportunityToConfigureFlutterTextureView() { // ---- Test setup ---- Host customMockHost = mock(Host.class); + when(customMockHost.getContext()).thenReturn(RuntimeEnvironment.application); + when(customMockHost.getActivity()).thenReturn(Robolectric.setupActivity(Activity.class)); + when(customMockHost.getLifecycle()).thenReturn(mock(Lifecycle.class)); + when(customMockHost.getFlutterShellArgs()).thenReturn(new FlutterShellArgs(new String[] {})); + when(customMockHost.getDartEntrypointFunctionName()).thenReturn("main"); + when(customMockHost.getAppBundlePath()).thenReturn("/fake/path"); + when(customMockHost.getInitialRoute()).thenReturn("/"); when(customMockHost.getRenderMode()).thenReturn(RenderMode.texture); + when(customMockHost.getTransparencyMode()).thenReturn(TransparencyMode.transparent); + when(customMockHost.provideFlutterEngine(any(Context.class))).thenReturn(mockFlutterEngine); + when(customMockHost.shouldAttachEngineToActivity()).thenReturn(true); + when(customMockHost.shouldDestroyEngineWithHost()).thenReturn(true); // Create the real object that we're testing. FlutterActivityAndFragmentDelegate delegate = From abc72933e70c3695b10580ae157c1321d912ba65 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Thu, 2 Apr 2020 17:15:45 -0700 Subject: [PATCH 249/386] [pipeline] Add trace event for lag between target and display times (#17384) This change also adds TimeRecorder which records time at the start of each frame to capture the latest vsync target display time and wires it in to the rasterizer to add trace events when there is a lag. --- fml/time/time_delta.h | 4 +++ fml/trace_event.cc | 55 ++++++++++++++---------------- fml/trace_event.h | 40 +++++++++++++++++++++- shell/common/animator.h | 2 +- shell/common/animator_unittests.cc | 8 +++-- shell/common/rasterizer.cc | 32 ++++++++++++++++- shell/common/rasterizer.h | 11 ++++++ shell/common/shell.cc | 16 +++++++-- shell/common/shell.h | 9 ++++- shell/common/shell_test.cc | 4 +++ shell/common/shell_test.h | 3 ++ 11 files changed, 147 insertions(+), 37 deletions(-) diff --git a/fml/time/time_delta.h b/fml/time/time_delta.h index 98a11b4100800..e54a1f8bf9bfa 100644 --- a/fml/time/time_delta.h +++ b/fml/time/time_delta.h @@ -55,6 +55,10 @@ class TimeDelta { return FromNanoseconds(seconds * (1000.0 * 1000.0 * 1000.0)); } + static constexpr TimeDelta FromMillisecondsF(double millis) { + return FromNanoseconds(millis * (1000.0 * 1000.0)); + } + constexpr int64_t ToNanoseconds() const { return delta_; } constexpr int64_t ToMicroseconds() const { return ToNanoseconds() / 1000; } constexpr int64_t ToMilliseconds() const { return ToMicroseconds() / 1000; } diff --git a/fml/trace_event.cc b/fml/trace_event.cc index 7e11b2c6f8e88..d055582d3a0eb 100644 --- a/fml/trace_event.cc +++ b/fml/trace_event.cc @@ -51,6 +51,7 @@ size_t TraceNonce() { void TraceTimelineEvent(TraceArg category_group, TraceArg name, + int64_t timestamp_micros, TraceIDArg identifier, Dart_Timeline_Event_Type type, const std::vector& c_names, @@ -66,7 +67,7 @@ void TraceTimelineEvent(TraceArg category_group, FlutterTimelineEvent( name, // label - Dart_TimelineGetMicros(), // timestamp0 + timestamp_micros, // timestamp0 identifier, // timestamp1_or_async_id type, // event type argument_count, // argument_count @@ -75,6 +76,22 @@ void TraceTimelineEvent(TraceArg category_group, ); } +void TraceTimelineEvent(TraceArg category_group, + TraceArg name, + TraceIDArg identifier, + Dart_Timeline_Event_Type type, + const std::vector& c_names, + const std::vector& values) { + TraceTimelineEvent(category_group, // group + name, // name + Dart_TimelineGetMicros(), // timestamp_micros + identifier, // identifier + type, // type + c_names, // names + values // values + ); +} + void TraceEvent0(TraceArg category_group, TraceArg name) { FlutterTimelineEvent(name, // label Dart_TimelineGetMicros(), // timestamp0 @@ -131,34 +148,6 @@ void TraceEventEnd(TraceArg name) { ); } -void TraceEventAsyncComplete(TraceArg category_group, - TraceArg name, - TimePoint begin, - TimePoint end) { - auto identifier = TraceNonce(); - - if (begin > end) { - std::swap(begin, end); - } - - FlutterTimelineEvent(name, // label - begin.ToEpochDelta().ToMicroseconds(), // timestamp0 - identifier, // timestamp1_or_async_id - Dart_Timeline_Event_Async_Begin, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values - ); - FlutterTimelineEvent(name, // label - end.ToEpochDelta().ToMicroseconds(), // timestamp0 - identifier, // timestamp1_or_async_id - Dart_Timeline_Event_Async_End, // event type - 0, // argument_count - nullptr, // argument_names - nullptr // argument_values - ); -} - void TraceEventAsyncBegin0(TraceArg category_group, TraceArg name, TraceIDArg id) { @@ -275,6 +264,14 @@ size_t TraceNonce() { return 0; } +void TraceTimelineEvent(TraceArg category_group, + TraceArg name, + int64_t timestamp_micros, + TraceIDArg identifier, + Dart_Timeline_Event_Type type, + const std::vector& c_names, + const std::vector& values) {} + void TraceTimelineEvent(TraceArg category_group, TraceArg name, TraceIDArg identifier, diff --git a/fml/trace_event.h b/fml/trace_event.h index a90aed2ff643e..98d328e134819 100644 --- a/fml/trace_event.h +++ b/fml/trace_event.h @@ -114,6 +114,14 @@ using TraceIDArg = int64_t; void TraceSetWhitelist(const std::vector& whitelist); +void TraceTimelineEvent(TraceArg category_group, + TraceArg name, + int64_t timestamp_micros, + TraceIDArg id, + Dart_Timeline_Event_Type type, + const std::vector& names, + const std::vector& values); + void TraceTimelineEvent(TraceArg category_group, TraceArg name, TraceIDArg id, @@ -209,10 +217,40 @@ void TraceEvent2(TraceArg category_group, void TraceEventEnd(TraceArg name); +template void TraceEventAsyncComplete(TraceArg category_group, TraceArg name, TimePoint begin, - TimePoint end); + TimePoint end, + Args... args) { + auto identifier = TraceNonce(); + const auto split = SplitArguments(args...); + + if (begin > end) { + std::swap(begin, end); + } + + const int64_t begin_micros = begin.ToEpochDelta().ToMicroseconds(); + const int64_t end_micros = end.ToEpochDelta().ToMicroseconds(); + + TraceTimelineEvent(category_group, // group + name, // name + begin_micros, // timestamp_micros + identifier, // identifier + Dart_Timeline_Event_Async_Begin, // type + split.first, // names + split.second // values + ); + + TraceTimelineEvent(category_group, // group + name, // name + end_micros, // timestamp_micros + identifier, // identifier + Dart_Timeline_Event_Async_End, // type + split.first, // names + split.second // values + ); +} void TraceEventAsyncBegin0(TraceArg category_group, TraceArg name, diff --git a/shell/common/animator.h b/shell/common/animator.h index ba5e1f4df40a3..f96acb7e9a3a0 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -30,7 +30,7 @@ class Animator final { public: class Delegate { public: - virtual void OnAnimatorBeginFrame(fml::TimePoint frame_time) = 0; + virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time) = 0; virtual void OnAnimatorNotifyIdle(int64_t deadline) = 0; diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index 3907cee3b89ab..ed54ea5272be2 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -84,10 +84,14 @@ TEST_F(ShellTest, VSyncTargetTime) { }); on_target_time_latch.Wait(); - ASSERT_EQ(ConstantFiringVsyncWaiter::frame_target_time.ToEpochDelta() - .ToMicroseconds(), + const auto vsync_waiter_target_time = + ConstantFiringVsyncWaiter::frame_target_time; + ASSERT_EQ(vsync_waiter_target_time.ToEpochDelta().ToMicroseconds(), target_time); + // validate that the latest target time has also been updated. + ASSERT_EQ(GetLatestFrameTargetTime(shell.get()), vsync_waiter_target_time); + // teardown. DestroyShell(std::move(shell), std::move(task_runners)); ASSERT_FALSE(DartVMRef::IsInstanceRunning()); diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 4d7d5ea669b2f..0561b0ce4e4ee 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -8,6 +8,8 @@ #include +#include "fml/time/time_delta.h" +#include "fml/time/time_point.h" #include "third_party/skia/include/core/SkEncodedImageFormat.h" #include "third_party/skia/include/core/SkImageEncoder.h" #include "third_party/skia/include/core/SkPictureRecorder.h" @@ -240,6 +242,7 @@ RasterStatus Rasterizer::DoDraw( } FrameTiming timing; + const fml::TimePoint frame_target_time = layer_tree->target_time(); timing.Set(FrameTiming::kBuildStart, layer_tree->build_start()); timing.Set(FrameTiming::kBuildFinish, layer_tree->build_finish()); timing.Set(FrameTiming::kRasterStart, fml::TimePoint::Now()); @@ -265,9 +268,36 @@ RasterStatus Rasterizer::DoDraw( // TODO(liyuqian): in Fuchsia, the rasterization doesn't finish when // Rasterizer::DoDraw finishes. Future work is needed to adapt the timestamp // for Fuchsia to capture SceneUpdateContext::ExecutePaintTasks. - timing.Set(FrameTiming::kRasterFinish, fml::TimePoint::Now()); + const auto raster_finish_time = fml::TimePoint::Now(); + timing.Set(FrameTiming::kRasterFinish, raster_finish_time); delegate_.OnFrameRasterized(timing); + if (raster_finish_time > frame_target_time) { + fml::TimePoint latest_frame_target_time = + delegate_.GetLatestFrameTargetTime(); + const auto frame_budget_millis = delegate_.GetFrameBudget().count(); + if (latest_frame_target_time < raster_finish_time) { + latest_frame_target_time = + latest_frame_target_time + + fml::TimeDelta::FromMillisecondsF(frame_budget_millis); + } + const auto frame_lag = + (latest_frame_target_time - frame_target_time).ToMillisecondsF(); + const int vsync_transitions_missed = round(frame_lag / frame_budget_millis); + fml::tracing::TraceEventAsyncComplete( + "flutter", // category + "SceneDisplayLag", // name + frame_target_time, // begin_time + raster_finish_time, // end_time + "frame_target_time", // arg_key_1 + frame_target_time, // arg_val_1 + "current_frame_target_time", // arg_key_2 + latest_frame_target_time, // arg_val_2 + "vsync_transitions_missed", // arg_key_3 + vsync_transitions_missed // arg_val_3 + ); + } + // Pipeline pressure is applied from a couple of places: // rasterizer: When there are more items as of the time of Consume. // animator (via shell): Frame gets produces every vsync. diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index f6bdd297ed8cc..d4d57c5efe117 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -19,6 +19,8 @@ #include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/shell/common/pipeline.h" #include "flutter/shell/common/surface.h" +#include "fml/time/time_delta.h" +#include "fml/time/time_point.h" namespace flutter { @@ -67,6 +69,10 @@ class Rasterizer final : public SnapshotDelegate { /// Time limit for a smooth frame. See `Engine::GetDisplayRefreshRate`. virtual fml::Milliseconds GetFrameBudget() = 0; + + /// Target time for the latest frame. See also `Shell::OnAnimatorBeginFrame` + /// for when this time gets updated. + virtual fml::TimePoint GetLatestFrameTargetTime() const = 0; }; // TODO(dnfield): remove once embedders have caught up. @@ -75,6 +81,11 @@ class Rasterizer final : public SnapshotDelegate { fml::Milliseconds GetFrameBudget() override { return fml::kDefaultFrameBudget; } + // Returning a time in the past so we don't add additional trace + // events when exceeding the frame budget for other embedders. + fml::TimePoint GetLatestFrameTargetTime() const override { + return fml::TimePoint::FromEpochDelta(fml::TimeDelta::Zero()); + } }; //---------------------------------------------------------------------------- diff --git a/shell/common/shell.cc b/shell/common/shell.cc index c09da8e4287c0..71ab4a95094d2 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -931,12 +931,17 @@ void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) { } // |Animator::Delegate| -void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_time) { +void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + // record the target time for use by rasterizer. + { + std::scoped_lock time_recorder_lock(time_recorder_mutex_); + latest_frame_target_time_.emplace(frame_target_time); + } if (engine_) { - engine_->BeginFrame(frame_time); + engine_->BeginFrame(frame_target_time); } } @@ -1165,6 +1170,13 @@ fml::Milliseconds Shell::GetFrameBudget() { } } +fml::TimePoint Shell::GetLatestFrameTargetTime() const { + std::scoped_lock time_recorder_lock(time_recorder_mutex_); + FML_CHECK(latest_frame_target_time_.has_value()) + << "GetLatestFrameTargetTime called before OnAnimatorBeginFrame"; + return latest_frame_target_time_.value(); +} + // |ServiceProtocol::Handler| fml::RefPtr Shell::GetServiceProtocolHandlerTaskRunner( std::string_view method) const { diff --git a/shell/common/shell.h b/shell/common/shell.h index d6345f115bde3..f8cbc85cd6c9a 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -6,6 +6,7 @@ #define SHELL_COMMON_SHELL_H_ #include +#include #include #include @@ -32,6 +33,7 @@ #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_io_manager.h" #include "flutter/shell/common/surface.h" +#include "fml/time/time_point.h" namespace flutter { @@ -368,6 +370,8 @@ class Shell final : public PlatformView::Delegate, const TaskRunners task_runners_; const Settings settings_; DartVMRef vm_; + mutable std::mutex time_recorder_mutex_; + std::optional latest_frame_target_time_; std::unique_ptr platform_view_; // on platform task runner std::unique_ptr engine_; // on UI task runner std::unique_ptr rasterizer_; // on GPU task runner @@ -478,7 +482,7 @@ class Shell final : public PlatformView::Delegate, void OnPlatformViewSetNextFrameCallback(const fml::closure& closure) override; // |Animator::Delegate| - void OnAnimatorBeginFrame(fml::TimePoint frame_time) override; + void OnAnimatorBeginFrame(fml::TimePoint frame_target_time) override; // |Animator::Delegate| void OnAnimatorNotifyIdle(int64_t deadline) override; @@ -517,6 +521,9 @@ class Shell final : public PlatformView::Delegate, // |Rasterizer::Delegate| fml::Milliseconds GetFrameBudget() override; + // |Rasterizer::Delegate| + fml::TimePoint GetLatestFrameTargetTime() const override; + // |ServiceProtocol::Handler| fml::RefPtr GetServiceProtocolHandlerTaskRunner( std::string_view method) const override; diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 5522feb591f79..378375c5eac1e 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -250,6 +250,10 @@ TaskRunners ShellTest::GetTaskRunnersForFixture() { }; } +fml::TimePoint ShellTest::GetLatestFrameTargetTime(Shell* shell) const { + return shell->GetLatestFrameTargetTime(); +} + std::unique_ptr ShellTest::CreateShell(Settings settings, bool simulate_vsync) { return CreateShell(std::move(settings), GetTaskRunnersForFixture(), diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 7d35853cdbec6..79eaf2554991c 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -19,6 +19,7 @@ #include "flutter/testing/elf_loader.h" #include "flutter/testing/test_dart_native_resolver.h" #include "flutter/testing/thread_test.h" +#include "fml/time/time_point.h" namespace flutter { namespace testing { @@ -37,6 +38,8 @@ class ShellTest : public ThreadTest { void DestroyShell(std::unique_ptr shell, TaskRunners task_runners); TaskRunners GetTaskRunnersForFixture(); + fml::TimePoint GetLatestFrameTargetTime(Shell* shell) const; + void SendEnginePlatformMessage(Shell* shell, fml::RefPtr message); From 288b66b1b663ce5c52a6b5f2a97a4b7af5e8a6ad Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 2 Apr 2020 18:16:05 -0700 Subject: [PATCH 250/386] Disconnect from platform views and virtual displays before detaching from the engine (#17379) --- .../io/flutter/app/FlutterPluginRegistry.java | 4 ++-- .../embedding/engine/FlutterEngine.java | 2 ++ .../platform/PlatformViewsController.java | 16 +++++++++++++- .../embedding/engine/FlutterEngineTest.java | 22 +++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java index b1979694cffdf..2a7173634ec6b 100644 --- a/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java +++ b/shell/platform/android/io/flutter/app/FlutterPluginRegistry.java @@ -86,7 +86,7 @@ public void attach(FlutterView flutterView, Activity activity) { public void detach() { mPlatformViewsController.detach(); - mPlatformViewsController.onFlutterViewDestroyed(); + mPlatformViewsController.onDetachedFromJNI(); mFlutterView = null; mActivity = null; } @@ -239,6 +239,6 @@ public boolean onViewDestroy(FlutterNativeView view) { } public void destroy() { - mPlatformViewsController.onFlutterViewDestroyed(); + mPlatformViewsController.onDetachedFromJNI(); } } diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index dfd2bd674c57c..271a1a8ec24dc 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -225,6 +225,7 @@ public FlutterEngine( textInputChannel = new TextInputChannel(dartExecutor); this.platformViewsController = platformViewsController; + this.platformViewsController.onAttachedToJNI(); this.pluginRegistry = new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader); @@ -289,6 +290,7 @@ public void destroy() { Log.v(TAG, "Destroying."); // The order that these things are destroyed is important. pluginRegistry.destroy(); + platformViewsController.onDetachedFromJNI(); dartExecutor.onDetachedFromJNI(); flutterJNI.removeEngineLifecycleListener(engineLifecycleListener); flutterJNI.detachFromNativeAndReleaseResources(); diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java index 6c87e20c0fd94..cc5dfc91af850 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java @@ -404,7 +404,21 @@ public PlatformViewRegistry getRegistry() { return registry; } - public void onFlutterViewDestroyed() { + /** + * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@link + * PlatformViewsController} attaches to JNI. + */ + public void onAttachedToJNI() { + // Currently no action needs to be taken after JNI attachment. + } + + /** + * Invoked when the {@link io.flutter.embedding.engine.FlutterEngine} that owns this {@link + * PlatformViewsController} detaches from JNI. + */ + public void onDetachedFromJNI() { + // Dispose all virtual displays so that any future updates to textures will not be + // propagated to the native peer. flushAllViews(); } diff --git a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java index c95612281f1c8..9a15364373b7a 100644 --- a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java +++ b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java @@ -102,6 +102,28 @@ public void itNotifiesPlatformViewsControllerWhenDevHotRestart() { verify(platformViewsController, times(1)).onPreEngineRestart(); } + @Test + public void itNotifiesPlatformViewsControllerAboutJNILifecycle() { + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + when(mockFlutterJNI.isAttached()).thenReturn(true); + + PlatformViewsController platformViewsController = mock(PlatformViewsController.class); + + // Execute behavior under test. + FlutterEngine engine = + new FlutterEngine( + RuntimeEnvironment.application, + mock(FlutterLoader.class), + mockFlutterJNI, + platformViewsController, + /*dartVmArgs=*/ new String[] {}, + /*automaticallyRegisterPlugins=*/ false); + verify(platformViewsController, times(1)).onAttachedToJNI(); + + engine.destroy(); + verify(platformViewsController, times(1)).onDetachedFromJNI(); + } + @Test public void itUsesApplicationContext() { Context context = mock(Context.class); From f5127cc07a762de774b7e941fe6eb402c97aa1b3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 21:21:04 -0400 Subject: [PATCH 251/386] Roll fuchsia/sdk/core/linux-amd64 from v61zg... to -jFTb... (#17490) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index cec9991598d1f..e6fec868ea892 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'v61zgYIe9NuuksbE4M1t2gGUKFkkbftlCx7cF7xRPOcC' + 'version': '-jFTb6Nn6MiakW6cUrYdtqfT5UjYr2rUZT82XjPsU8MC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 444c4c408b4b9..1a39b82bb0f06 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 18c6c9c5873393a0ab127ace3b75f16e +Signature: e65524c6da33f0aa327a259ebe9582f9 UNUSED LICENSES: From 258433388de1fdf22306e7e45c0816d01e62cba2 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 2 Apr 2020 21:26:02 -0400 Subject: [PATCH 252/386] Roll src/third_party/skia 8afde5f39508..8efbbbc0d1d4 (14 commits) (#17491) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/DEPS b/DEPS index e6fec868ea892..7fafc0e50b90b 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '8afde5f395088351ea43045a380e2feb55bf88ea', + 'skia_revision': '8efbbbc0d1d4f05dae43e69dc81ea244e1b686d9', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 879f13e9efa7e..5a0986f6a31d9 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 9d9bbbc53975fe3640278f517c6d165c +Signature: 6955ae4ede67f0b8724b26a9ff9344f6 UNUSED LICENSES: @@ -3629,7 +3629,6 @@ FILE: ../../../third_party/skia/modules/skottie/gm/ExternalProperties.cpp FILE: ../../../third_party/skia/modules/skottie/gm/SkottieGM.cpp FILE: ../../../third_party/skia/modules/skottie/include/Skottie.h FILE: ../../../third_party/skia/modules/skottie/src/Skottie.cpp -FILE: ../../../third_party/skia/modules/skottie/src/SkottieValue.cpp FILE: ../../../third_party/skia/modules/skottie/src/SkottieValue.h FILE: ../../../third_party/skia/modules/sksg/include/SkSGDraw.h FILE: ../../../third_party/skia/modules/sksg/include/SkSGEffectNode.h @@ -5602,13 +5601,14 @@ FILE: ../../../third_party/skia/modules/skottie/src/Transform.cpp FILE: ../../../third_party/skia/modules/skottie/src/Transform.h FILE: ../../../third_party/skia/modules/skottie/src/animator/Animator.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/Animator.h -FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.cpp -FILE: ../../../third_party/skia/modules/skottie/src/animator/Keyframe.h -FILE: ../../../third_party/skia/modules/skottie/src/animator/Scalar.cpp -FILE: ../../../third_party/skia/modules/skottie/src/animator/Shape.cpp -FILE: ../../../third_party/skia/modules/skottie/src/animator/Vec2.cpp -FILE: ../../../third_party/skia/modules/skottie/src/animator/Vector.cpp -FILE: ../../../third_party/skia/modules/skottie/src/animator/Vector.h +FILE: ../../../third_party/skia/modules/skottie/src/animator/KeyframeAnimator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/KeyframeAnimator.h +FILE: ../../../third_party/skia/modules/skottie/src/animator/ScalarKeyframeAnimator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/ShapeKeyframeAnimator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/TextKeyframeAnimator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp +FILE: ../../../third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Ellipse.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/FillStroke.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Gradient.cpp From 03d5e500037fb091c9f044a067fca84466a191e5 Mon Sep 17 00:00:00 2001 From: Joshua Seaton Date: Thu, 2 Apr 2020 18:44:09 -0700 Subject: [PATCH 253/386] [tools][fuchsia] Do not tar debug symbol CIPD uploads (#17275) Fuchsia is the main consumer of these CIPD packages and - to simplify its infrastructure - it is migrating its SDK partners over to producing a CIPD package containing a flat .build-id directory. This change also updates the CIPD package so that the .build-id directory is placed at the root. Bug: fxbug.dev/41443 --- .../fuchsia/merge_and_upload_debug_symbols.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tools/fuchsia/merge_and_upload_debug_symbols.py b/tools/fuchsia/merge_and_upload_debug_symbols.py index 3bf5d852ca972..00a71e38ede1a 100755 --- a/tools/fuchsia/merge_and_upload_debug_symbols.py +++ b/tools/fuchsia/merge_and_upload_debug_symbols.py @@ -73,16 +73,22 @@ def ProcessCIPDPackage(upload, cipd_yaml, engine_version, out_dir, target_arch): if tries == num_tries - 1: raise -def CreateTarFile(folder_path, base_dir): - archive_name = os.path.basename(folder_path) - tar_file_path = os.path.join(base_dir, archive_name + '.tar.bz2') - with tarfile.open(tar_file_path, "w:bz2") as archive: - for root, dirs, _ in os.walk(folder_path): - for dir_name in dirs: - dir_path = os.path.join(root, dir_name) - archive.add(dir_path, arcname=dir_name) - return tar_file_path - +# Recursively hardlinks contents from one directory to another, +# skipping over collisions. +def HardlinkContents(dirA, dirB): + for src_dir, _, filenames in os.walk(dirA): + for filename in filenames: + src = os.path.join(src_dir, filename) + dest_dir = os.path.join(dirB, os.path.relpath(src_dir, dirA)) + os.makedirs(dest_dir) + dest = os.path.join(dest_dir, filename) + if os.path.exists(dest): + # The last two path components provide a content address for a .build-id entry. + tokens = os.path.split(dest) + name = os.path.join(tokens[-2], tokens[-1]) + print('%s already exists in destination; skipping linking' % name) + continue + os.link(src, dest) def main(): parser = argparse.ArgumentParser() @@ -114,18 +120,17 @@ def main(): for symbol_dir in symbol_dirs: assert os.path.exists(symbol_dir) and os.path.isdir(symbol_dir) - arch = args.target_arch - out_dir = os.path.join(args.out_dir, - 'flutter-fuchsia-debug-symbols-%s' % arch) + out_dir = args.out_dir + if os.path.exists(out_dir): print 'Directory: %s is not empty, deleting it.' % out_dir shutil.rmtree(out_dir) os.makedirs(out_dir) for symbol_dir in symbol_dirs: - archive_path = CreateTarFile(symbol_dir, out_dir) - print('Created archive: ' + archive_path) + HardlinkContents(symbol_dir, out_dir) + arch = args.target_arch cipd_def = WriteCIPDDefinition(arch, out_dir) ProcessCIPDPackage(args.upload, cipd_def, args.engine_version, out_dir, arch) return 0 From e26622ae68d5afe3d59fda7fec27eabac39b1cfe Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 3 Apr 2020 12:44:15 -0400 Subject: [PATCH 254/386] Roll src/third_party/dart fae35fca47c9..e736495eb7f0 (26 commits) (#17494) https://dart.googlesource.com/sdk.git/+log/fae35fca47c9..e736495eb7f0 git log fae35fca47c9..e736495eb7f0 --date=short --first-parent --format='%ad %ae %s' 2020-04-03 rnystrom@google.com Migrate language_2/const to NNBD. 2020-04-03 srujzs@google.com [dart:html] Copy and rename lib_2/html to NNBD. 2020-04-02 jwren@google.com Bug fix in inherited_reference_contributor.dart, completions inside of ConstructorDeclarations should include inherited members. 2020-04-02 scheglov@google.com Infer when nullable type parameter type. 2020-04-02 rnystrom@google.com Add a "fork" command to the migration tool. 2020-04-02 alexmarkov@google.com [tests/nnbd] Fix lib/convert/chunked_conversion_utf88_test for strong mode 2020-04-02 srujzs@google.com [dart:html] Add insertAll to ChildrenElementList 2020-04-02 liama@google.com [nnbd/tests] Fix more HTTP/socket strong mode tests 2020-04-02 srujzs@google.com [dart:html] Remove nullability from CssClassSet funcs 2020-04-02 srujzs@google.com [dart:html] Fix nullability for vars with null checks 2020-04-02 scheglov@google.com More work on ConstraintMatchingTest. 2020-04-02 rnystrom@google.com Migrate language_2/covariant_override to NNBD. 2020-04-02 alexmarkov@google.com Update pkg/vm patterns so pkg/vm_service doesn't match them 2020-04-02 brianwilkerson@google.com Clean up OpTypeTest 2020-04-02 sigmund@google.com [tests] adjust error_stack_trace2_test to work also for DDC. 2020-04-02 sstrickl@google.com [vm] Drop WSRs with reachable references, not just allocated. 2020-04-02 rnystrom@google.com Migrate language_2/closure to NNBD. 2020-04-02 liama@google.com [nnbd/lib] Fix some more strong mode tests 2020-04-02 regis@google.com [VM/nnbd] Minor cleanup when checking required named parameter in strong mode. 2020-04-02 alexmarkov@google.com [vm/nnbd] Infer more accurate types for late static fields 2020-04-02 brianwilkerson@google.com Remove an unnecessary type annotation to satisfy a lint 2020-04-02 brianwilkerson@google.com Report more development time metrics 2020-04-02 scheglov@google.com Remove typeParameterType() and replace with typeParameterTypeStar() mostly. 2020-04-02 brianwilkerson@google.com Pull the latest version of pedantic into the SDK 2020-04-02 scheglov@google.com Remove unused TypeComparison. 2020-04-02 scheglov@google.com Replace interfaceType() with interfaceTypeNone() in ConstraintMatchingTest. Created with: gclient setdep -r src/third_party/dart@e736495eb7f0 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 4 ++-- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DEPS b/DEPS index 7fafc0e50b90b..d34db594e9234 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'fae35fca47c9fed7a2339cf2e9535d526146ddd2', + 'dart_revision': 'e736495eb7f0d50163b62fe880e1676c18407dc9', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -73,7 +73,7 @@ vars = { 'dart_observatory_pub_packages_rev': '0894122173b0f98eb08863a7712e78407d4477bc', 'dart_package_config_tag': 'v1.9.2', 'dart_path_tag': '1.6.2', - 'dart_pedantic_tag': 'v1.8.0', + 'dart_pedantic_tag': 'v1.9.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', 'dart_pub_rev': '3606265962da4248d34d352aa3d170aae4496a90', diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 5622db71b633c..1faba5a457daa 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 6a8cdbfab22fe5596c1dd7b9f6696c39 +Signature: 09fd94fb21d08b06304aff361f8a5015 UNUSED LICENSES: From dfedac61c180c5b9084396d3cd9a3105b00d96d3 Mon Sep 17 00:00:00 2001 From: George Wright Date: Fri, 3 Apr 2020 10:54:23 -0700 Subject: [PATCH 255/386] Revert "[tools][fuchsia] Do not tar debug symbol CIPD uploads (#17275)" This reverts commit 03d5e500037fb091c9f044a067fca84466a191e5. --- .../fuchsia/merge_and_upload_debug_symbols.py | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/tools/fuchsia/merge_and_upload_debug_symbols.py b/tools/fuchsia/merge_and_upload_debug_symbols.py index 00a71e38ede1a..3bf5d852ca972 100755 --- a/tools/fuchsia/merge_and_upload_debug_symbols.py +++ b/tools/fuchsia/merge_and_upload_debug_symbols.py @@ -73,22 +73,16 @@ def ProcessCIPDPackage(upload, cipd_yaml, engine_version, out_dir, target_arch): if tries == num_tries - 1: raise -# Recursively hardlinks contents from one directory to another, -# skipping over collisions. -def HardlinkContents(dirA, dirB): - for src_dir, _, filenames in os.walk(dirA): - for filename in filenames: - src = os.path.join(src_dir, filename) - dest_dir = os.path.join(dirB, os.path.relpath(src_dir, dirA)) - os.makedirs(dest_dir) - dest = os.path.join(dest_dir, filename) - if os.path.exists(dest): - # The last two path components provide a content address for a .build-id entry. - tokens = os.path.split(dest) - name = os.path.join(tokens[-2], tokens[-1]) - print('%s already exists in destination; skipping linking' % name) - continue - os.link(src, dest) +def CreateTarFile(folder_path, base_dir): + archive_name = os.path.basename(folder_path) + tar_file_path = os.path.join(base_dir, archive_name + '.tar.bz2') + with tarfile.open(tar_file_path, "w:bz2") as archive: + for root, dirs, _ in os.walk(folder_path): + for dir_name in dirs: + dir_path = os.path.join(root, dir_name) + archive.add(dir_path, arcname=dir_name) + return tar_file_path + def main(): parser = argparse.ArgumentParser() @@ -120,17 +114,18 @@ def main(): for symbol_dir in symbol_dirs: assert os.path.exists(symbol_dir) and os.path.isdir(symbol_dir) - out_dir = args.out_dir - + arch = args.target_arch + out_dir = os.path.join(args.out_dir, + 'flutter-fuchsia-debug-symbols-%s' % arch) if os.path.exists(out_dir): print 'Directory: %s is not empty, deleting it.' % out_dir shutil.rmtree(out_dir) os.makedirs(out_dir) for symbol_dir in symbol_dirs: - HardlinkContents(symbol_dir, out_dir) + archive_path = CreateTarFile(symbol_dir, out_dir) + print('Created archive: ' + archive_path) - arch = args.target_arch cipd_def = WriteCIPDDefinition(arch, out_dir) ProcessCIPDPackage(args.upload, cipd_def, args.engine_version, out_dir, arch) return 0 From 83e493ae6262b5a0f5b7366ef761976b5d9c4141 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 3 Apr 2020 11:00:57 -0700 Subject: [PATCH 256/386] Revert "[tools][fuchsia] Do not tar debug symbol CIPD uploads (#17275)" (#17501) Breaks CIPD package creation on Fuchsia. Example error message: Directory: /b/s/w/ir/k/recipe_cleanup/tmp9x3dHw is not empty, deleting it. Traceback (most recent call last): File "/b/s/w/ir/cache/builder/src/flutter/tools/fuchsia/merge_and_upload_debug_symbols.py", line 140, in sys.exit(main()) File "/b/s/w/ir/cache/builder/src/flutter/tools/fuchsia/merge_and_upload_debug_symbols.py", line 131, in main HardlinkContents(symbol_dir, out_dir) File "/b/s/w/ir/cache/builder/src/flutter/tools/fuchsia/merge_and_upload_debug_symbols.py", line 83, in HardlinkContents os.makedirs(dest_dir) File "/b/s/w/ir/cache/vpython/9f7888/lib/python2.7/os.py", line 157, in makedirs mkdir(name, mode) OSError: [Errno 17] File exists: '/b/s/w/ir/k/recipe_cleanup/tmp9x3dHw/.' This reverts commit 03d5e500037fb091c9f044a067fca84466a191e5. From e55b4476f1657590783ca04865d7b354fa4559dc Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 3 Apr 2020 11:44:35 -0700 Subject: [PATCH 257/386] Use const refs in for loops where reasonable (#17484) This patch optimizes C++11 range-based for loops where the variable is copied in each iteration but it would suffice to obtain it by const reference. This is only applied to loop variables of types that are expensive to copy which means they are not trivially copyable or have a non-trivial copy constructor or destructor. To ensure that it is safe to replace the copy with a const reference only the following cases are modified: * The loop variable is const-qualified. * The loop variable is not const, but only const methods or operators are invoked on it, or it is used as const reference or value argument in constructors or function calls. This is an application of the internal performance-for-range-copy clang-tidy analysis. --- runtime/dart_service_isolate.cc | 2 +- third_party/txt/src/txt/font_collection.cc | 5 +++-- third_party/txt/src/txt/font_features.cc | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/runtime/dart_service_isolate.cc b/runtime/dart_service_isolate.cc index 218c3bb6035d9..5632738a10891 100644 --- a/runtime/dart_service_isolate.cc +++ b/runtime/dart_service_isolate.cc @@ -76,7 +76,7 @@ void DartServiceIsolate::NotifyServerState(Dart_NativeArguments args) { } } - for (auto callback_to_fire : callbacks_to_fire) { + for (const auto& callback_to_fire : callbacks_to_fire) { callback_to_fire(uri); } } diff --git a/third_party/txt/src/txt/font_collection.cc b/third_party/txt/src/txt/font_collection.cc index 3b550f7f313fb..c35c10a95f7e5 100644 --- a/third_party/txt/src/txt/font_collection.cc +++ b/third_party/txt/src/txt/font_collection.cc @@ -151,7 +151,7 @@ FontCollection::GetMinikinFontCollectionForFamilies( // Search for default font family if no user font families were found. if (minikin_families.empty()) { const auto default_font_families = GetDefaultFontFamilies(); - for (auto family : default_font_families) { + for (const auto& family : default_font_families) { std::shared_ptr minikin_family = FindFontFamilyInManagers(family); if (minikin_family != nullptr) { @@ -166,7 +166,8 @@ FontCollection::GetMinikinFontCollectionForFamilies( return nullptr; } if (enable_font_fallback_) { - for (std::string fallback_family : fallback_fonts_for_locale_[locale]) { + for (const std::string& fallback_family : + fallback_fonts_for_locale_[locale]) { auto it = fallback_fonts_.find(fallback_family); if (it != fallback_fonts_.end()) { minikin_families.push_back(it->second); diff --git a/third_party/txt/src/txt/font_features.cc b/third_party/txt/src/txt/font_features.cc index 0729a035aa9fb..0b7b6abdc2b83 100644 --- a/third_party/txt/src/txt/font_features.cc +++ b/third_party/txt/src/txt/font_features.cc @@ -30,7 +30,7 @@ std::string FontFeatures::GetFeatureSettings() const { std::ostringstream stream; - for (auto kv : feature_map_) { + for (const auto& kv : feature_map_) { if (stream.tellp()) { stream << ','; } From e2b86556be301c759a6d84a984301e8fa6c8e69e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 3 Apr 2020 14:51:05 -0400 Subject: [PATCH 258/386] Roll src/third_party/skia 8efbbbc0d1d4..e70e0c055f56 (1 commits) (#17496) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d34db594e9234..ea8644e465ec1 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '8efbbbc0d1d4f05dae43e69dc81ea244e1b686d9', + 'skia_revision': 'e70e0c055f565368c666d079297c0ddc5fb08642', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 5a0986f6a31d9..6ff0f7f4f9fa6 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 6955ae4ede67f0b8724b26a9ff9344f6 +Signature: aa0ebc07dd8bfea67e5dd3d4133daf47 UNUSED LICENSES: From a9c6222cdc311132083f8a54d6146a6a45cfc147 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 3 Apr 2020 14:56:06 -0400 Subject: [PATCH 259/386] Roll fuchsia/sdk/core/mac-amd64 from 6V5BR... to tKvUB... (#17497) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index ea8644e465ec1..7d068d952c4b7 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '6V5BRZbu7X78QeWu5MLPsTSEe87gD9AjXtxEhUkf4JAC' + 'version': 'tKvUBSZoYR-8s8xKYPqEnhBACvjbfOfU1Lxx_cP_-XYC' } ], 'condition': 'host_os == "mac"', From fa09075127f34dc18a95ce51088205052599ae33 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 3 Apr 2020 15:01:02 -0400 Subject: [PATCH 260/386] Roll fuchsia/sdk/core/linux-amd64 from -jFTb... to TWx2R... (#17498) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7d068d952c4b7..0e40351f8cff1 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': '-jFTb6Nn6MiakW6cUrYdtqfT5UjYr2rUZT82XjPsU8MC' + 'version': 'TWx2Rkn7JXtD4kl2fw_lug0JHAPYt-lB0TcNFj0UIG0C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 1a39b82bb0f06..5471a3d395809 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: e65524c6da33f0aa327a259ebe9582f9 +Signature: ae577c7fe76b9b277b8fc274e52bd1bb UNUSED LICENSES: From 983d1e17031acaadee8137d4e97bdafa49deb179 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Fri, 3 Apr 2020 12:33:54 -0700 Subject: [PATCH 261/386] Fix include paths of fml/time headers in the shell and rasterizer (#17502) --- shell/common/rasterizer.cc | 4 ++-- shell/common/rasterizer.h | 4 ++-- shell/common/shell.h | 2 +- shell/common/shell_test.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 0561b0ce4e4ee..f360cefe960f0 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -8,8 +8,8 @@ #include -#include "fml/time/time_delta.h" -#include "fml/time/time_point.h" +#include "flutter/fml/time/time_delta.h" +#include "flutter/fml/time/time_point.h" #include "third_party/skia/include/core/SkEncodedImageFormat.h" #include "third_party/skia/include/core/SkImageEncoder.h" #include "third_party/skia/include/core/SkPictureRecorder.h" diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index d4d57c5efe117..dac22644db0ab 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -16,11 +16,11 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/raster_thread_merger.h" #include "flutter/fml/synchronization/waitable_event.h" +#include "flutter/fml/time/time_delta.h" +#include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/snapshot_delegate.h" #include "flutter/shell/common/pipeline.h" #include "flutter/shell/common/surface.h" -#include "fml/time/time_delta.h" -#include "fml/time/time_point.h" namespace flutter { diff --git a/shell/common/shell.h b/shell/common/shell.h index f8cbc85cd6c9a..b5245527ff6c6 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -22,6 +22,7 @@ #include "flutter/fml/synchronization/sync_switch.h" #include "flutter/fml/synchronization/waitable_event.h" #include "flutter/fml/thread.h" +#include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" #include "flutter/lib/ui/window/platform_message.h" @@ -33,7 +34,6 @@ #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_io_manager.h" #include "flutter/shell/common/surface.h" -#include "fml/time/time_point.h" namespace flutter { diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 79eaf2554991c..680db7d349c1d 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -11,6 +11,7 @@ #include "flutter/flow/layers/container_layer.h" #include "flutter/fml/build_config.h" #include "flutter/fml/macros.h" +#include "flutter/fml/time/time_point.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" @@ -19,7 +20,6 @@ #include "flutter/testing/elf_loader.h" #include "flutter/testing/test_dart_native_resolver.h" #include "flutter/testing/thread_test.h" -#include "fml/time/time_point.h" namespace flutter { namespace testing { From 09bc1fc45e505118e96afedd7ba365e593369697 Mon Sep 17 00:00:00 2001 From: xster Date: Fri, 3 Apr 2020 13:03:06 -0700 Subject: [PATCH 262/386] Small updates to objcdocs (#17151) --- .../ios/framework/Headers/FlutterEngine.h | 5 ++++ .../Headers/FlutterHeadlessDartRunner.h | 2 +- .../framework/Headers/FlutterViewController.h | 26 ++++++++++++------- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h index 51782ceca07cb..46980d609a078 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h @@ -36,6 +36,11 @@ extern NSString* const FlutterDefaultDartEntrypoint; * `FlutterViewController` instances to maintain state and/or asynchronous tasks * (such as downloading a large file). * + * A FlutterEngine can also be used to prewarm the Dart execution environment and reduce the + * latency of showing the Flutter screen when a `FlutterViewController` is created and presented. + * See http://flutter.dev/docs/development/add-to-app/performance for more details on loading + * performance. + * * Alternatively, you can simply create a new `FlutterViewController` with only a * `FlutterDartProject`. That `FlutterViewController` will internally manage its * own instance of a FlutterEngine, but will not guarantee survival of the engine diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h index 24acbc08ad6fe..1994cfc81f954 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h @@ -22,7 +22,7 @@ typedef void (^FlutterHeadlessDartRunnerCallback)(BOOL success); /** - * The FlutterHeadlessDartRunner runs Flutter Dart code with a null rasterizer, + * The deprecated FlutterHeadlessDartRunner runs Flutter Dart code with a null rasterizer, * and no native drawing surface. It is appropriate for use in running Dart * code e.g. in the background from a plugin. * diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h index 9753cdb68bca0..77bf219f128a0 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h @@ -35,14 +35,17 @@ extern NSNotificationName const FlutterSemanticsUpdateNotification; * handled by `FlutterEngine`. Calls on this class to those members all proxy through to the * `FlutterEngine` attached FlutterViewController. * - * A FlutterViewController can be initialized either with an already-running `FlutterEngine` via - * the `initWithEngine:` initializer, or it can be initialized with a `FlutterDartProject` that - * will be used to implicitly spin up a new `FlutterEngine`. Creating a `FlutterEngine before - * showing a `FlutterViewController` can be used to pre-initialize the Dart VM and to prepare the - * isolate in order to reduce the latency to the first rendered frame. Holding a `FlutterEngine` - * independently of FlutterViewControllers can also be used to not to lose Dart-related state and - * asynchronous tasks when navigating back and forth between a FlutterViewController and other - * `UIViewController`s. + * A FlutterViewController can be initialized either with an already-running `FlutterEngine` via the + * `initWithEngine:` initializer, or it can be initialized with a `FlutterDartProject` that will be + * used to implicitly spin up a new `FlutterEngine`. Creating a `FlutterEngine before showing a + * FlutterViewController can be used to pre-initialize the Dart VM and to prepare the isolate in + * order to reduce the latency to the first rendered frame. See + * https://flutter.dev/docs/development/add-to-app/performance for more details on loading + * latency. + * + * Holding a `FlutterEngine` independently of FlutterViewControllers can also be used to not to lose + * Dart-related state and asynchronous tasks when navigating back and forth between a + * FlutterViewController and other `UIViewController`s. */ FLUTTER_EXPORT @interface FlutterViewController : UIViewController @@ -64,6 +67,9 @@ FLUTTER_EXPORT * Initializes a new FlutterViewController and `FlutterEngine` with the specified * `FlutterDartProject`. * + * This will implicitly create a new `FlutterEngine` which is retrievable via the `engine` property + * after initialization. + * * @param project The `FlutterDartProject` to initialize the `FlutterEngine` with. * @param nibName The NIB name to initialize this UIViewController with. * @param nibBundle The NIB bundle. @@ -174,7 +180,9 @@ FLUTTER_EXPORT @property(nonatomic, getter=isViewOpaque) BOOL viewOpaque; /** - * The `FlutterEngine` instance for this view controller. + * The `FlutterEngine` instance for this view controller. This could be the engine this + * `FlutterViewController` is initialized with or a new `FlutterEngine` implicitly created if + * no engine was supplied during initialization. */ @property(weak, nonatomic, readonly) FlutterEngine* engine; From d5e7b807be0ccf7fbebf63f50c6133ea6ee5fc42 Mon Sep 17 00:00:00 2001 From: xster Date: Fri, 3 Apr 2020 13:03:26 -0700 Subject: [PATCH 263/386] onRequestPermissionsResult now require calling super on AndroidX master (#17305) From 4cfbe45033ef2788512932fd75d51c84036c204a Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 3 Apr 2020 14:35:48 -0700 Subject: [PATCH 264/386] Started clearing out the parent of orphaned semantic objects. (#17499) --- .../framework/Source/accessibility_bridge.h | 6 ++- .../framework/Source/accessibility_bridge.mm | 39 ++++++++++++++++--- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h index d6e0bb6446e0c..772c84eb5ef0e 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h @@ -43,7 +43,7 @@ class AccessibilityBridge; * The parent of this node in the node tree. Will be nil for the root node and * during transient state changes. */ -@property(nonatomic, assign) SemanticsObject* parent; +@property(nonatomic, readonly) SemanticsObject* parent; /** * The accessibility bridge that this semantics object is attached to. This @@ -85,13 +85,15 @@ class AccessibilityBridge; * Direct children of this semantics object. Each child's `parent` property must * be equal to this object. */ -@property(nonatomic, strong) NSMutableArray* children; +@property(nonatomic, strong) NSArray* children; /** * Used if this SemanticsObject is for a platform view. */ @property(strong, nonatomic) FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer; +- (void)replaceChildAtIndex:(NSInteger)index withChild:(SemanticsObject*)child; + - (BOOL)nodeWillCauseLayoutChange:(const flutter::SemanticsNode*)node; #pragma mark - Designated initializers diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 53f097e65b029..46224c1ea4da7 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -14,6 +14,8 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" +FLUTTER_ASSERT_NOT_ARC + namespace { constexpr int32_t kRootNodeId = 0; @@ -162,8 +164,14 @@ - (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject @end +@interface SemanticsObject () +/** Should only be called in conjunction with setting child/parent relationship. */ +- (void)privateSetParent:(SemanticsObject*)parent; +@end + @implementation SemanticsObject { fml::scoped_nsobject _container; + NSMutableArray* _children; } #pragma mark - Override base class designated initializers @@ -197,7 +205,7 @@ - (instancetype)initWithBridge:(fml::WeakPtr)bridg - (void)dealloc { for (SemanticsObject* child in _children) { - child.parent = nil; + [child privateSetParent:nil]; } [_children removeAllObjects]; [_children release]; @@ -239,6 +247,28 @@ - (BOOL)hasChildren { return [self.children count] != 0; } +- (void)privateSetParent:(SemanticsObject*)parent { + _parent = parent; +} + +- (void)setChildren:(NSArray*)children { + for (SemanticsObject* child in _children) { + [child privateSetParent:nil]; + } + [_children release]; + _children = [[NSMutableArray alloc] initWithArray:children]; + for (SemanticsObject* child in _children) { + [child privateSetParent:self]; + } +} + +- (void)replaceChildAtIndex:(NSInteger)index withChild:(SemanticsObject*)child { + SemanticsObject* oldChild = _children[index]; + [oldChild privateSetParent:nil]; + [child privateSetParent:self]; + [_children replaceObjectAtIndex:index withObject:child]; +} + #pragma mark - UIAccessibility overrides - (BOOL)isAccessibilityElement { @@ -653,7 +683,7 @@ - (NSInteger)indexOfAccessibilityElement:(id)element { return ((FlutterPlatformViewSemanticsContainer*)element).index; } - NSMutableArray* children = [_semanticsObject children]; + NSArray* children = [_semanticsObject children]; for (size_t i = 0; i < [children count]; i++) { SemanticsObject* child = children[i]; if ((![child hasChildren] && child == element) || @@ -741,7 +771,6 @@ - (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { [[[NSMutableArray alloc] initWithCapacity:newChildCount] autorelease]; for (NSUInteger i = 0; i < newChildCount; ++i) { SemanticsObject* child = GetOrCreateObject(node.childrenInTraversalOrder[i], nodes); - child.parent = object; [newChildren addObject:child]; } object.children = newChildren; @@ -847,10 +876,8 @@ static void ReplaceSemanticsObject(SemanticsObject* oldObject, assert(oldObject.node.id == newObject.node.id); NSNumber* nodeId = @(oldObject.node.id); NSUInteger positionInChildlist = [oldObject.parent.children indexOfObject:oldObject]; - SemanticsObject* parent = oldObject.parent; [objects removeObjectForKey:nodeId]; - newObject.parent = parent; - [newObject.parent.children replaceObjectAtIndex:positionInChildlist withObject:newObject]; + [oldObject.parent replaceChildAtIndex:positionInChildlist withChild:newObject]; objects[nodeId] = newObject; } From a93382fe22f60330e903e0d432f5a0ad5d634231 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 3 Apr 2020 18:51:02 -0400 Subject: [PATCH 265/386] Roll src/third_party/skia e70e0c055f56..cc8a76f3c763 (23 commits) (#17504) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/DEPS b/DEPS index 0e40351f8cff1..60e32d6f74d43 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'e70e0c055f565368c666d079297c0ddc5fb08642', + 'skia_revision': 'cc8a76f3c763b503e3042db807c5cb1e33d6fe10', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 6ff0f7f4f9fa6..73911dde0680b 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: aa0ebc07dd8bfea67e5dd3d4133daf47 +Signature: 1d3e051c6ddd101b0625007dc5354da8 UNUSED LICENSES: @@ -2514,12 +2514,12 @@ FILE: ../../../third_party/skia/src/gpu/vk/GrVkGpu.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkGpu.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkImage.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkImage.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkIndexBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkIndexBuffer.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkInterface.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkInterface.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkMemory.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkMemory.h +FILE: ../../../third_party/skia/src/gpu/vk/GrVkMeshBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/vk/GrVkMeshBuffer.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkRenderPass.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkRenderPass.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkRenderTarget.cpp @@ -2536,8 +2536,6 @@ FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformBuffer.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkUniformBuffer.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkUtil.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkUtil.h -FILE: ../../../third_party/skia/src/gpu/vk/GrVkVertexBuffer.cpp -FILE: ../../../third_party/skia/src/gpu/vk/GrVkVertexBuffer.h FILE: ../../../third_party/skia/src/image/SkImage_Gpu.h FILE: ../../../third_party/skia/src/image/SkImage_Lazy.cpp FILE: ../../../third_party/skia/src/opts/Sk4px_NEON.h @@ -3497,6 +3495,7 @@ FILE: ../../../third_party/skia/src/pdf/SkClusterator.h FILE: ../../../third_party/skia/src/pdf/SkPDFTag.cpp FILE: ../../../third_party/skia/src/pdf/SkPDFTag.h FILE: ../../../third_party/skia/src/ports/SkFontMgr_fuchsia.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLByteCode.cpp FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.cpp FILE: ../../../third_party/skia/src/sksl/SkSLCPPUniformCTypes.h FILE: ../../../third_party/skia/src/sksl/SkSLPipelineStageCodeGenerator.cpp @@ -3918,7 +3917,6 @@ FILE: ../../../third_party/skia/src/gpu/effects/GrDeviceSpaceEffect.fp FILE: ../../../third_party/skia/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp FILE: ../../../third_party/skia/src/gpu/effects/generated/GrDeviceSpaceEffect.h FILE: ../../../third_party/skia/src/gpu/vk/GrVkManagedResource.h -FILE: ../../../third_party/skia/src/sksl/SkSLInterpreter.h FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVtoHLSL.cpp FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVtoHLSL.h ---------------------------------------------------------------------------------------------------- From d5a9e77adf9ee855cbda330d3d04a4a17f996fb1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 3 Apr 2020 19:16:02 -0400 Subject: [PATCH 266/386] Roll src/third_party/dart e736495eb7f0..f144d5fdca56 (31 commits) (#17503) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 60e32d6f74d43..c3cf66e5a8956 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'e736495eb7f0d50163b62fe880e1676c18407dc9', + 'dart_revision': 'f144d5fdca56e53b80f858140620982aec7e2eea', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 1faba5a457daa..e830ead29b486 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 09fd94fb21d08b06304aff361f8a5015 +Signature: 68c93925c73188171c45b4ad5b310f0c UNUSED LICENSES: @@ -7796,9 +7796,11 @@ FILE: ../../../third_party/dart/runtime/vm/visitor.cc FILE: ../../../third_party/dart/samples/ffi/async/async_test.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_async_callback.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_native_port_call.dart +FILE: ../../../third_party/dart/sdk/lib/_http/embedder_config.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart +FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/embedder_config.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/js_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/vm/lib/ffi_struct_patch.dart From bac37de43d392e7e099bf0262ab1ef32cac04bb9 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Fri, 3 Apr 2020 16:54:33 -0700 Subject: [PATCH 267/386] refactor: split up accessibility bridge and semantics object (#17507) --- ci/licenses_golden/licenses_flutter | 3 + shell/platform/darwin/ios/BUILD.gn | 2 + .../ios/framework/Source/SemanticsObject.h | 156 ++++ .../ios/framework/Source/SemanticsObject.mm | 706 +++++++++++++++++ .../framework/Source/accessibility_bridge.h | 146 +--- .../framework/Source/accessibility_bridge.mm | 713 ------------------ .../Source/accessibility_bridge_ios.h | 32 + .../Source/accessibility_text_entry.mm | 3 +- 8 files changed, 909 insertions(+), 852 deletions(-) create mode 100644 shell/platform/darwin/ios/framework/Source/SemanticsObject.h create mode 100644 shell/platform/darwin/ios/framework/Source/SemanticsObject.mm create mode 100644 shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 724af2afdc5e0..08d65c11b2a61 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -874,8 +874,11 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 7af51b5aa230d..c98e2b31bf321 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -73,6 +73,8 @@ shared_library("create_flutter_framework_dylib") { "framework/Source/FlutterView.mm", "framework/Source/FlutterViewController.mm", "framework/Source/FlutterViewController_Internal.h", + "framework/Source/SemanticsObject.h", + "framework/Source/SemanticsObject.mm", "framework/Source/accessibility_bridge.h", "framework/Source/accessibility_bridge.mm", "framework/Source/accessibility_text_entry.h", diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.h b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h new file mode 100644 index 0000000000000..575b978ab2a32 --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h @@ -0,0 +1,156 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_SEMANTICS_OBJECT_H_ +#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_SEMANTICS_OBJECT_H_ + +#import + +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/lib/ui/semantics/semantics_node.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h" + +constexpr int32_t kRootNodeId = 0; + +@class FlutterPlatformViewSemanticsContainer; + +/** + * A node in the iOS semantics tree. + */ +@interface SemanticsObject : UIAccessibilityElement + +/** + * The globally unique identifier for this node. + */ +@property(nonatomic, readonly) int32_t uid; + +/** + * The parent of this node in the node tree. Will be nil for the root node and + * during transient state changes. + */ +@property(nonatomic, readonly) SemanticsObject* parent; + +/** + * The accessibility bridge that this semantics object is attached to. This + * object may use the bridge to access contextual application information. A weak + * pointer is used because the platform view owns the accessibility bridge. + * If you are referencing this property from an iOS callback, be sure to + * use `isAccessibilityBridgeActive` to protect against the case where this + * node may be orphaned. + */ +@property(nonatomic, readonly) fml::WeakPtr bridge; + +/** + * Due to the fact that VoiceOver may hold onto SemanticObjects even after it shuts down, + * there can be situations where the AccessibilityBridge is shutdown, but the SemanticObject + * will still be alive. If VoiceOver is turned on again, it may try to access this orphaned + * SemanticObject. Methods that are called from the accessiblity framework should use + * this to guard against this case by just returning early if its bridge has been shutdown. + * + * See https://github.com/flutter/flutter/issues/43795 for more information. + */ +- (BOOL)isAccessibilityBridgeAlive; + +/** + * The semantics node used to produce this semantics object. + */ +@property(nonatomic, readonly) flutter::SemanticsNode node; + +/** + * Updates this semantics object using data from the `node` argument. + */ +- (void)setSemanticsNode:(const flutter::SemanticsNode*)node NS_REQUIRES_SUPER; + +/** + * Whether this semantics object has child semantics objects. + */ +@property(nonatomic, readonly) BOOL hasChildren; + +/** + * Direct children of this semantics object. Each child's `parent` property must + * be equal to this object. + */ +@property(nonatomic, strong) NSArray* children; + +/** + * Used if this SemanticsObject is for a platform view. + */ +@property(strong, nonatomic) FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer; + +- (void)replaceChildAtIndex:(NSInteger)index withChild:(SemanticsObject*)child; + +- (BOOL)nodeWillCauseLayoutChange:(const flutter::SemanticsNode*)node; + +#pragma mark - Designated initializers + +- (instancetype)init __attribute__((unavailable("Use initWithBridge instead"))); +- (instancetype)initWithBridge:(fml::WeakPtr)bridge + uid:(int32_t)uid NS_DESIGNATED_INITIALIZER; + +- (BOOL)nodeWillCauseScroll:(const flutter::SemanticsNode*)node; +- (void)collectRoutes:(NSMutableArray*)edges; +- (NSString*)routeName; + +@end + +/** + * An implementation of UIAccessibilityCustomAction which also contains the + * Flutter uid. + */ +@interface FlutterCustomAccessibilityAction : UIAccessibilityCustomAction + +/** + * The uid of the action defined by the flutter application. + */ +@property(nonatomic) int32_t uid; + +@end + +/** + * The default implementation of `SemanticsObject` for most accessibility elements + * in the iOS accessibility tree. + * + * Use this implementation for nodes that do not need to be expressed via UIKit-specific + * protocols (it only implements NSObject). + * + * See also: + * * TextInputSemanticsObject, which implements `UITextInput` protocol to expose + * editable text widgets to a11y. + */ +@interface FlutterSemanticsObject : SemanticsObject +@end + +/** + * Designated to act as an accessibility container of a platform view. + * + * This object does not take any accessibility actions on its own, nor has any accessibility + * label/value/trait/hint... on its own. The accessibility data will be handled by the platform + * view. + * + * See also: + * * `SemanticsObject` for the other type of semantics objects. + * * `FlutterSemanticsObject` for default implementation of `SemanticsObject`. + */ +@interface FlutterPlatformViewSemanticsContainer : UIAccessibilityElement + +/** + * The position inside an accessibility container. + */ +@property(nonatomic) NSInteger index; + +- (instancetype)init __attribute__((unavailable("Use initWithAccessibilityContainer: instead"))); + +- (instancetype)initWithSemanticsObject:(SemanticsObject*)object; + +@end + +/// A proxy class for SemanticsObject and UISwitch. For most Accessibility and +/// SemanticsObject methods it delegates to the semantics object, otherwise it +/// sends messages to the UISwitch. +@interface FlutterSwitchSemanticsObject : UISwitch +- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject; +@end + +#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_SEMANTICS_OBJECT_H_ diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm new file mode 100644 index 0000000000000..19deaf223ee9c --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm @@ -0,0 +1,706 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" + +#include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" + +namespace { + +flutter::SemanticsAction GetSemanticsActionForScrollDirection( + UIAccessibilityScrollDirection direction) { + // To describe the vertical scroll direction, UIAccessibilityScrollDirection uses the + // direction the scroll bar moves in and SemanticsAction uses the direction the finger + // moves in. However, the horizontal scroll direction matches the SemanticsAction direction. + // That is way the following maps vertical opposite of the SemanticsAction, but the horizontal + // maps directly. + switch (direction) { + case UIAccessibilityScrollDirectionRight: + case UIAccessibilityScrollDirectionPrevious: // TODO(abarth): Support RTL using + // _node.textDirection. + return flutter::SemanticsAction::kScrollRight; + case UIAccessibilityScrollDirectionLeft: + case UIAccessibilityScrollDirectionNext: // TODO(abarth): Support RTL using + // _node.textDirection. + return flutter::SemanticsAction::kScrollLeft; + case UIAccessibilityScrollDirectionUp: + return flutter::SemanticsAction::kScrollDown; + case UIAccessibilityScrollDirectionDown: + return flutter::SemanticsAction::kScrollUp; + } + FML_DCHECK(false); // Unreachable + return flutter::SemanticsAction::kScrollUp; +} + +} // namespace + +@implementation FlutterSwitchSemanticsObject { + SemanticsObject* _semanticsObject; +} + +- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject { + self = [super init]; + if (self) { + _semanticsObject = [semanticsObject retain]; + } + return self; +} + +- (void)dealloc { + [_semanticsObject release]; + [super dealloc]; +} + +- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel { + NSMethodSignature* result = [super methodSignatureForSelector:sel]; + if (!result) { + result = [_semanticsObject methodSignatureForSelector:sel]; + } + return result; +} + +- (void)forwardInvocation:(NSInvocation*)anInvocation { + [anInvocation setTarget:_semanticsObject]; + [anInvocation invoke]; +} + +- (CGRect)accessibilityFrame { + return [_semanticsObject accessibilityFrame]; +} + +- (id)accessibilityContainer { + return [_semanticsObject accessibilityContainer]; +} + +- (NSString*)accessibilityLabel { + return [_semanticsObject accessibilityLabel]; +} + +- (NSString*)accessibilityHint { + return [_semanticsObject accessibilityHint]; +} + +- (NSString*)accessibilityValue { + if ([_semanticsObject node].HasFlag(flutter::SemanticsFlags::kIsToggled) || + [_semanticsObject node].HasFlag(flutter::SemanticsFlags::kIsChecked)) { + self.on = YES; + } else { + self.on = NO; + } + + if (![_semanticsObject isAccessibilityBridgeAlive]) { + return nil; + } else { + return [super accessibilityValue]; + } +} + +@end // FlutterSwitchSemanticsObject + +@implementation FlutterCustomAccessibilityAction { +} +@end + +/** + * Represents a semantics object that has children and hence has to be presented to the OS as a + * UIAccessibilityContainer. + * + * The SemanticsObject class cannot implement the UIAccessibilityContainer protocol because an + * object that returns YES for isAccessibilityElement cannot also implement + * UIAccessibilityContainer. + * + * With the help of SemanticsObjectContainer, the hierarchy of semantic objects received from + * the framework, such as: + * + * SemanticsObject1 + * SemanticsObject2 + * SemanticsObject3 + * SemanticsObject4 + * + * is translated into the following hierarchy, which is understood by iOS: + * + * SemanticsObjectContainer1 + * SemanticsObject1 + * SemanticsObjectContainer2 + * SemanticsObject2 + * SemanticsObject3 + * SemanticsObject4 + * + * From Flutter's view of the world (the first tree seen above), we construct iOS's view of the + * world (second tree) as follows: We replace each SemanticsObjects that has children with a + * SemanticsObjectContainer, which has the original SemanticsObject and its children as children. + * + * SemanticsObjects have semantic information attached to them which is interpreted by + * VoiceOver (they return YES for isAccessibilityElement). The SemanticsObjectContainers are just + * there for structure and they don't provide any semantic information to VoiceOver (they return + * NO for isAccessibilityElement). + */ +@interface SemanticsObjectContainer : UIAccessibilityElement +- (instancetype)init __attribute__((unavailable("Use initWithSemanticsObject instead"))); +- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject + bridge:(fml::WeakPtr)bridge + NS_DESIGNATED_INITIALIZER; + +@property(nonatomic, weak) SemanticsObject* semanticsObject; + +@end + +@interface SemanticsObject () +/** Should only be called in conjunction with setting child/parent relationship. */ +- (void)privateSetParent:(SemanticsObject*)parent; +@end + +@implementation SemanticsObject { + fml::scoped_nsobject _container; + NSMutableArray* _children; +} + +#pragma mark - Override base class designated initializers + +// Method declared as unavailable in the interface +- (instancetype)init { + [self release]; + [super doesNotRecognizeSelector:_cmd]; + return nil; +} + +#pragma mark - Designated initializers + +- (instancetype)initWithBridge:(fml::WeakPtr)bridge + uid:(int32_t)uid { + FML_DCHECK(bridge) << "bridge must be set"; + FML_DCHECK(uid >= kRootNodeId); + // Initialize with the UIView as the container. + // The UIView will not necessarily be accessibility parent for this object. + // The bridge informs the OS of the actual structure via + // `accessibilityContainer` and `accessibilityElementAtIndex`. + self = [super initWithAccessibilityContainer:bridge->view()]; + + if (self) { + _bridge = bridge; + _uid = uid; + _children = [[NSMutableArray alloc] init]; + } + + return self; +} + +- (void)dealloc { + for (SemanticsObject* child in _children) { + [child privateSetParent:nil]; + } + [_children removeAllObjects]; + [_children release]; + _parent = nil; + _container.get().semanticsObject = nil; + [_platformViewSemanticsContainer release]; + [super dealloc]; +} + +#pragma mark - Semantic object methods + +- (BOOL)isAccessibilityBridgeAlive { + return [self bridge].get() != nil; +} + +- (void)setSemanticsNode:(const flutter::SemanticsNode*)node { + _node = *node; +} + +/** + * Whether calling `setSemanticsNode:` with `node` would cause a layout change. + */ +- (BOOL)nodeWillCauseLayoutChange:(const flutter::SemanticsNode*)node { + return [self node].rect != node->rect || [self node].transform != node->transform; +} + +/** + * Whether calling `setSemanticsNode:` with `node` would cause a scroll event. + */ +- (BOOL)nodeWillCauseScroll:(const flutter::SemanticsNode*)node { + return !isnan([self node].scrollPosition) && !isnan(node->scrollPosition) && + [self node].scrollPosition != node->scrollPosition; +} + +- (BOOL)hasChildren { + if (_node.IsPlatformViewNode()) { + return YES; + } + return [self.children count] != 0; +} + +- (void)privateSetParent:(SemanticsObject*)parent { + _parent = parent; +} + +- (void)setChildren:(NSArray*)children { + for (SemanticsObject* child in _children) { + [child privateSetParent:nil]; + } + [_children release]; + _children = [[NSMutableArray alloc] initWithArray:children]; + for (SemanticsObject* child in _children) { + [child privateSetParent:self]; + } +} + +- (void)replaceChildAtIndex:(NSInteger)index withChild:(SemanticsObject*)child { + SemanticsObject* oldChild = _children[index]; + [oldChild privateSetParent:nil]; + [child privateSetParent:self]; + [_children replaceObjectAtIndex:index withObject:child]; +} + +#pragma mark - UIAccessibility overrides + +- (BOOL)isAccessibilityElement { + if (![self isAccessibilityBridgeAlive]) + return false; + + // Note: hit detection will only apply to elements that report + // -isAccessibilityElement of YES. The framework will continue scanning the + // entire element tree looking for such a hit. + + // We enforce in the framework that no other useful semantics are merged with these nodes. + if ([self node].HasFlag(flutter::SemanticsFlags::kScopesRoute)) + return false; + + // If the only flag(s) set are scrolling related AND + // The only flags set are not kIsHidden OR + // The node doesn't have a label, value, or hint OR + // The only actions set are scrolling related actions. + // + // The kIsHidden flag set with any other flag just means this node is now + // hidden but still is a valid target for a11y focus in the tree, e.g. a list + // item that is currently off screen but the a11y navigation needs to know + // about. + return (([self node].flags & ~flutter::kScrollableSemanticsFlags) != 0 && + [self node].flags != static_cast(flutter::SemanticsFlags::kIsHidden)) || + ![self node].label.empty() || ![self node].value.empty() || ![self node].hint.empty() || + ([self node].actions & ~flutter::kScrollableSemanticsActions) != 0; +} + +- (void)collectRoutes:(NSMutableArray*)edges { + if ([self node].HasFlag(flutter::SemanticsFlags::kScopesRoute)) + [edges addObject:self]; + if ([self hasChildren]) { + for (SemanticsObject* child in self.children) { + [child collectRoutes:edges]; + } + } +} + +- (BOOL)onCustomAccessibilityAction:(FlutterCustomAccessibilityAction*)action { + if (![self node].HasAction(flutter::SemanticsAction::kCustomAction)) + return NO; + int32_t action_id = action.uid; + std::vector args; + args.push_back(3); // type=int32. + args.push_back(action_id); + args.push_back(action_id >> 8); + args.push_back(action_id >> 16); + args.push_back(action_id >> 24); + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kCustomAction, + std::move(args)); + return YES; +} + +- (NSString*)routeName { + // Returns the first non-null and non-empty semantic label of a child + // with an NamesRoute flag. Otherwise returns nil. + if ([self node].HasFlag(flutter::SemanticsFlags::kNamesRoute)) { + NSString* newName = [self accessibilityLabel]; + if (newName != nil && [newName length] > 0) { + return newName; + } + } + if ([self hasChildren]) { + for (SemanticsObject* child in self.children) { + NSString* newName = [child routeName]; + if (newName != nil && [newName length] > 0) { + return newName; + } + } + } + return nil; +} + +- (NSString*)accessibilityLabel { + if (![self isAccessibilityBridgeAlive]) + return nil; + + if ([self node].label.empty()) + return nil; + return @([self node].label.data()); +} + +- (NSString*)accessibilityHint { + if (![self isAccessibilityBridgeAlive]) + return nil; + + if ([self node].hint.empty()) + return nil; + return @([self node].hint.data()); +} + +- (NSString*)accessibilityValue { + if (![self isAccessibilityBridgeAlive]) + return nil; + + if (![self node].value.empty()) { + return @([self node].value.data()); + } + + // FlutterSwitchSemanticsObject should supercede these conditionals. + if ([self node].HasFlag(flutter::SemanticsFlags::kHasToggledState) || + [self node].HasFlag(flutter::SemanticsFlags::kHasCheckedState)) { + if ([self node].HasFlag(flutter::SemanticsFlags::kIsToggled) || + [self node].HasFlag(flutter::SemanticsFlags::kIsChecked)) { + return @"1"; + } else { + return @"0"; + } + } + + return nil; +} + +- (CGRect)accessibilityFrame { + if (![self isAccessibilityBridgeAlive]) + return CGRectMake(0, 0, 0, 0); + + if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden)) { + return [super accessibilityFrame]; + } + return [self globalRect]; +} + +- (CGRect)globalRect { + SkMatrix44 globalTransform = [self node].transform; + for (SemanticsObject* parent = [self parent]; parent; parent = parent.parent) { + globalTransform = parent.node.transform * globalTransform; + } + + SkPoint quad[4]; + [self node].rect.toQuad(quad); + for (auto& point : quad) { + SkScalar vector[4] = {point.x(), point.y(), 0, 1}; + globalTransform.mapScalars(vector); + point.set(vector[0] / vector[3], vector[1] / vector[3]); + } + SkRect rect; + rect.setBounds(quad, 4); + + // `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in + // the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to + // convert. + CGFloat scale = [[[self bridge]->view() window] screen].scale; + auto result = + CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale); + return UIAccessibilityConvertFrameToScreenCoordinates(result, [self bridge]->view()); +} + +#pragma mark - UIAccessibilityElement protocol + +- (id)accessibilityContainer { + if ([self hasChildren] || [self uid] == kRootNodeId) { + if (_container == nil) + _container.reset([[SemanticsObjectContainer alloc] initWithSemanticsObject:self + bridge:[self bridge]]); + return _container.get(); + } + if ([self parent] == nil) { + // This can happen when we have released the accessibility tree but iOS is + // still holding onto our objects. iOS can take some time before it + // realizes that the tree has changed. + return nil; + } + return [[self parent] accessibilityContainer]; +} + +#pragma mark - UIAccessibilityAction overrides + +- (BOOL)accessibilityActivate { + if (![self isAccessibilityBridgeAlive]) + return NO; + if (![self node].HasAction(flutter::SemanticsAction::kTap)) + return NO; + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kTap); + return YES; +} + +- (void)accessibilityIncrement { + if (![self isAccessibilityBridgeAlive]) + return; + if ([self node].HasAction(flutter::SemanticsAction::kIncrease)) { + [self node].value = [self node].increasedValue; + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kIncrease); + } +} + +- (void)accessibilityDecrement { + if (![self isAccessibilityBridgeAlive]) + return; + if ([self node].HasAction(flutter::SemanticsAction::kDecrease)) { + [self node].value = [self node].decreasedValue; + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDecrease); + } +} + +- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { + if (![self isAccessibilityBridgeAlive]) + return NO; + flutter::SemanticsAction action = GetSemanticsActionForScrollDirection(direction); + if (![self node].HasAction(action)) + return NO; + [self bridge]->DispatchSemanticsAction([self uid], action); + return YES; +} + +- (BOOL)accessibilityPerformEscape { + if (![self isAccessibilityBridgeAlive]) + return NO; + if (![self node].HasAction(flutter::SemanticsAction::kDismiss)) + return NO; + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDismiss); + return YES; +} + +#pragma mark UIAccessibilityFocus overrides + +- (void)accessibilityElementDidBecomeFocused { + if (![self isAccessibilityBridgeAlive]) + return; + if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden)) { + [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kShowOnScreen); + } + if ([self node].HasAction(flutter::SemanticsAction::kDidGainAccessibilityFocus)) { + [self bridge]->DispatchSemanticsAction([self uid], + flutter::SemanticsAction::kDidGainAccessibilityFocus); + } +} + +- (void)accessibilityElementDidLoseFocus { + if (![self isAccessibilityBridgeAlive]) + return; + if ([self node].HasAction(flutter::SemanticsAction::kDidLoseAccessibilityFocus)) { + [self bridge]->DispatchSemanticsAction([self uid], + flutter::SemanticsAction::kDidLoseAccessibilityFocus); + } +} + +@end + +@implementation FlutterSemanticsObject { +} + +#pragma mark - Override base class designated initializers + +// Method declared as unavailable in the interface +- (instancetype)init { + [self release]; + [super doesNotRecognizeSelector:_cmd]; + return nil; +} + +#pragma mark - Designated initializers + +- (instancetype)initWithBridge:(fml::WeakPtr)bridge + uid:(int32_t)uid { + self = [super initWithBridge:bridge uid:uid]; + return self; +} + +#pragma mark - UIAccessibility overrides + +- (UIAccessibilityTraits)accessibilityTraits { + UIAccessibilityTraits traits = UIAccessibilityTraitNone; + if ([self node].HasAction(flutter::SemanticsAction::kIncrease) || + [self node].HasAction(flutter::SemanticsAction::kDecrease)) { + traits |= UIAccessibilityTraitAdjustable; + } + // FlutterSwitchSemanticsObject should supercede these conditionals. + if ([self node].HasFlag(flutter::SemanticsFlags::kHasToggledState) || + [self node].HasFlag(flutter::SemanticsFlags::kHasCheckedState)) { + traits |= UIAccessibilityTraitButton; + } + if ([self node].HasFlag(flutter::SemanticsFlags::kIsSelected)) { + traits |= UIAccessibilityTraitSelected; + } + if ([self node].HasFlag(flutter::SemanticsFlags::kIsButton)) { + traits |= UIAccessibilityTraitButton; + } + if ([self node].HasFlag(flutter::SemanticsFlags::kHasEnabledState) && + ![self node].HasFlag(flutter::SemanticsFlags::kIsEnabled)) { + traits |= UIAccessibilityTraitNotEnabled; + } + if ([self node].HasFlag(flutter::SemanticsFlags::kIsHeader)) { + traits |= UIAccessibilityTraitHeader; + } + if ([self node].HasFlag(flutter::SemanticsFlags::kIsImage)) { + traits |= UIAccessibilityTraitImage; + } + if ([self node].HasFlag(flutter::SemanticsFlags::kIsLiveRegion)) { + traits |= UIAccessibilityTraitUpdatesFrequently; + } + if ([self node].HasFlag(flutter::SemanticsFlags::kIsLink)) { + traits |= UIAccessibilityTraitLink; + } + return traits; +} + +@end + +@implementation FlutterPlatformViewSemanticsContainer { + SemanticsObject* _semanticsObject; + UIView* _platformView; +} + +// Method declared as unavailable in the interface +- (instancetype)init { + [self release]; + [super doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithSemanticsObject:(SemanticsObject*)object { + FML_CHECK(object); + // Initialize with the UIView as the container. + // The UIView will not necessarily be accessibility parent for this object. + // The bridge informs the OS of the actual structure via + // `accessibilityContainer` and `accessibilityElementAtIndex`. + if (self = [super initWithAccessibilityContainer:object.bridge->view()]) { + _semanticsObject = object; + flutter::FlutterPlatformViewsController* controller = + object.bridge->GetPlatformViewsController(); + if (controller) { + _platformView = [controller->GetPlatformViewByID(object.node.platformViewId) view]; + } + self.accessibilityElements = @[ _semanticsObject, _platformView ]; + } + return self; +} + +- (CGRect)accessibilityFrame { + return _semanticsObject.accessibilityFrame; +} + +- (BOOL)isAccessibilityElement { + return NO; +} + +- (id)accessibilityContainer { + return [_semanticsObject accessibilityContainer]; +} + +- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { + return [_platformView accessibilityScroll:direction]; +} + +@end + +@implementation SemanticsObjectContainer { + SemanticsObject* _semanticsObject; + fml::WeakPtr _bridge; +} + +#pragma mark - initializers + +// Method declared as unavailable in the interface +- (instancetype)init { + [self release]; + [super doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject + bridge:(fml::WeakPtr)bridge { + FML_DCHECK(semanticsObject) << "semanticsObject must be set"; + // Initialize with the UIView as the container. + // The UIView will not necessarily be accessibility parent for this object. + // The bridge informs the OS of the actual structure via + // `accessibilityContainer` and `accessibilityElementAtIndex`. + self = [super initWithAccessibilityContainer:bridge->view()]; + + if (self) { + _semanticsObject = semanticsObject; + _bridge = bridge; + } + + return self; +} + +#pragma mark - UIAccessibilityContainer overrides + +- (NSInteger)accessibilityElementCount { + NSInteger count = [[_semanticsObject children] count] + 1; + return count; +} + +- (nullable id)accessibilityElementAtIndex:(NSInteger)index { + if (index < 0 || index >= [self accessibilityElementCount]) + return nil; + if (index == 0) { + return _semanticsObject; + } + + SemanticsObject* child = [_semanticsObject children][index - 1]; + + // Swap the original `SemanticsObject` to a `PlatformViewSemanticsContainer` + if (child.node.IsPlatformViewNode()) { + child.platformViewSemanticsContainer.index = index; + return child.platformViewSemanticsContainer; + } + + if ([child hasChildren]) + return [child accessibilityContainer]; + return child; +} + +- (NSInteger)indexOfAccessibilityElement:(id)element { + if (element == _semanticsObject) + return 0; + + // FlutterPlatformViewSemanticsContainer is always the last element of its parent. + if ([element isKindOfClass:[FlutterPlatformViewSemanticsContainer class]]) { + return ((FlutterPlatformViewSemanticsContainer*)element).index; + } + + NSArray* children = [_semanticsObject children]; + for (size_t i = 0; i < [children count]; i++) { + SemanticsObject* child = children[i]; + if ((![child hasChildren] && child == element) || + ([child hasChildren] && [child accessibilityContainer] == element)) + return i + 1; + } + return NSNotFound; +} + +#pragma mark - UIAccessibilityElement protocol + +- (BOOL)isAccessibilityElement { + return NO; +} + +- (CGRect)accessibilityFrame { + return [_semanticsObject accessibilityFrame]; +} + +- (id)accessibilityContainer { + if (!_bridge) { + return nil; + } + return ([_semanticsObject uid] == kRootNodeId) + ? _bridge->view() + : [[_semanticsObject parent] accessibilityContainer]; +} + +#pragma mark - UIAccessibilityAction overrides + +- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { + return [_semanticsObject accessibilityScroll:direction]; +} + +@end diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h index 772c84eb5ef0e..6b7d61fe1f23e 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h @@ -20,145 +20,15 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "third_party/skia/include/core/SkRect.h" -namespace flutter { -class AccessibilityBridge; -} // namespace flutter - -@class FlutterPlatformViewSemanticsContainer; - -/** - * A node in the iOS semantics tree. - */ -@interface SemanticsObject : UIAccessibilityElement - -/** - * The globally unique identifier for this node. - */ -@property(nonatomic, readonly) int32_t uid; - -/** - * The parent of this node in the node tree. Will be nil for the root node and - * during transient state changes. - */ -@property(nonatomic, readonly) SemanticsObject* parent; - -/** - * The accessibility bridge that this semantics object is attached to. This - * object may use the bridge to access contextual application information. A weak - * pointer is used because the platform view owns the accessibility bridge. - * If you are referencing this property from an iOS callback, be sure to - * use `isAccessibilityBridgeActive` to protect against the case where this - * node may be orphaned. - */ -@property(nonatomic, readonly) fml::WeakPtr bridge; - -/** - * Due to the fact that VoiceOver may hold onto SemanticObjects even after it shuts down, - * there can be situations where the AccessibilityBridge is shutdown, but the SemanticObject - * will still be alive. If VoiceOver is turned on again, it may try to access this orphaned - * SemanticObject. Methods that are called from the accessiblity framework should use - * this to guard against this case by just returning early if its bridge has been shutdown. - * - * See https://github.com/flutter/flutter/issues/43795 for more information. - */ -- (BOOL)isAccessibilityBridgeAlive; - -/** - * The semantics node used to produce this semantics object. - */ -@property(nonatomic, readonly) flutter::SemanticsNode node; - -/** - * Updates this semantics object using data from the `node` argument. - */ -- (void)setSemanticsNode:(const flutter::SemanticsNode*)node NS_REQUIRES_SUPER; - -/** - * Whether this semantics object has child semantics objects. - */ -@property(nonatomic, readonly) BOOL hasChildren; - -/** - * Direct children of this semantics object. Each child's `parent` property must - * be equal to this object. - */ -@property(nonatomic, strong) NSArray* children; - -/** - * Used if this SemanticsObject is for a platform view. - */ -@property(strong, nonatomic) FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer; - -- (void)replaceChildAtIndex:(NSInteger)index withChild:(SemanticsObject*)child; - -- (BOOL)nodeWillCauseLayoutChange:(const flutter::SemanticsNode*)node; - -#pragma mark - Designated initializers - -- (instancetype)init __attribute__((unavailable("Use initWithBridge instead"))); -- (instancetype)initWithBridge:(fml::WeakPtr)bridge - uid:(int32_t)uid NS_DESIGNATED_INITIALIZER; - -@end - -/** - * An implementation of UIAccessibilityCustomAction which also contains the - * Flutter uid. - */ -@interface FlutterCustomAccessibilityAction : UIAccessibilityCustomAction - -/** - * The uid of the action defined by the flutter application. - */ -@property(nonatomic) int32_t uid; - -@end - -/** - * The default implementation of `SemanticsObject` for most accessibility elements - * in the iOS accessibility tree. - * - * Use this implementation for nodes that do not need to be expressed via UIKit-specific - * protocols (it only implements NSObject). - * - * See also: - * * TextInputSemanticsObject, which implements `UITextInput` protocol to expose - * editable text widgets to a11y. - */ -@interface FlutterSemanticsObject : SemanticsObject -@end - -/** - * Designated to act as an accessibility container of a platform view. - * - * This object does not take any accessibility actions on its own, nor has any accessibility - * label/value/trait/hint... on its own. The accessibility data will be handled by the platform - * view. - * - * See also: - * * `SemanticsObject` for the other type of semantics objects. - * * `FlutterSemanticsObject` for default implementation of `SemanticsObject`. - */ -@interface FlutterPlatformViewSemanticsContainer : UIAccessibilityElement - -/** - * The position inside an accessibility container. - */ -@property(nonatomic) NSInteger index; - -- (instancetype)init __attribute__((unavailable("Use initWithAccessibilityContainer: instead"))); - -- (instancetype)initWithSemanticsObject:(SemanticsObject*)object; - -@end - namespace flutter { class PlatformViewIOS; -class AccessibilityBridge final { +class AccessibilityBridge final : public AccessibilityBridgeIos { public: AccessibilityBridge(UIView* view, PlatformViewIOS* platform_view, @@ -167,18 +37,18 @@ class AccessibilityBridge final { void UpdateSemantics(flutter::SemanticsNodeUpdates nodes, flutter::CustomAccessibilityActionUpdates actions); - void DispatchSemanticsAction(int32_t id, flutter::SemanticsAction action); + void DispatchSemanticsAction(int32_t id, flutter::SemanticsAction action) override; void DispatchSemanticsAction(int32_t id, flutter::SemanticsAction action, - std::vector args); + std::vector args) override; - UIView* textInputView(); + UIView* textInputView() override; - UIView* view() const { return view_; } + UIView* view() const override { return view_; } fml::WeakPtr GetWeakPtr(); - FlutterPlatformViewsController* GetPlatformViewsController() const { + FlutterPlatformViewsController* GetPlatformViewsController() const override { return platform_views_controller_; }; diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 46224c1ea4da7..2957489fb42fa 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -5,723 +5,10 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h" #include "flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h" -#include -#include - -#import - -#include "flutter/fml/logging.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" FLUTTER_ASSERT_NOT_ARC -namespace { - -constexpr int32_t kRootNodeId = 0; - -flutter::SemanticsAction GetSemanticsActionForScrollDirection( - UIAccessibilityScrollDirection direction) { - // To describe the vertical scroll direction, UIAccessibilityScrollDirection uses the - // direction the scroll bar moves in and SemanticsAction uses the direction the finger - // moves in. However, the horizontal scroll direction matches the SemanticsAction direction. - // That is way the following maps vertical opposite of the SemanticsAction, but the horizontal - // maps directly. - switch (direction) { - case UIAccessibilityScrollDirectionRight: - case UIAccessibilityScrollDirectionPrevious: // TODO(abarth): Support RTL using - // _node.textDirection. - return flutter::SemanticsAction::kScrollRight; - case UIAccessibilityScrollDirectionLeft: - case UIAccessibilityScrollDirectionNext: // TODO(abarth): Support RTL using - // _node.textDirection. - return flutter::SemanticsAction::kScrollLeft; - case UIAccessibilityScrollDirectionUp: - return flutter::SemanticsAction::kScrollDown; - case UIAccessibilityScrollDirectionDown: - return flutter::SemanticsAction::kScrollUp; - } - FML_DCHECK(false); // Unreachable - return flutter::SemanticsAction::kScrollUp; -} - -} // namespace - -/// A proxy class for SemanticsObject and UISwitch. For most Accessibility and -/// SemanticsObject methods it delegates to the semantics object, otherwise it -/// sends messages to the UISwitch. -@interface FlutterSwitchSemanticsObject : UISwitch -@end - -@implementation FlutterSwitchSemanticsObject { - SemanticsObject* _semanticsObject; -} - -- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject { - self = [super init]; - if (self) { - _semanticsObject = [semanticsObject retain]; - } - return self; -} - -- (void)dealloc { - [_semanticsObject release]; - [super dealloc]; -} - -- (NSMethodSignature*)methodSignatureForSelector:(SEL)sel { - NSMethodSignature* result = [super methodSignatureForSelector:sel]; - if (!result) { - result = [_semanticsObject methodSignatureForSelector:sel]; - } - return result; -} - -- (void)forwardInvocation:(NSInvocation*)anInvocation { - [anInvocation setTarget:_semanticsObject]; - [anInvocation invoke]; -} - -- (CGRect)accessibilityFrame { - return [_semanticsObject accessibilityFrame]; -} - -- (id)accessibilityContainer { - return [_semanticsObject accessibilityContainer]; -} - -- (NSString*)accessibilityLabel { - return [_semanticsObject accessibilityLabel]; -} - -- (NSString*)accessibilityHint { - return [_semanticsObject accessibilityHint]; -} - -- (NSString*)accessibilityValue { - if ([_semanticsObject node].HasFlag(flutter::SemanticsFlags::kIsToggled) || - [_semanticsObject node].HasFlag(flutter::SemanticsFlags::kIsChecked)) { - self.on = YES; - } else { - self.on = NO; - } - - if (![_semanticsObject isAccessibilityBridgeAlive]) { - return nil; - } else { - return [super accessibilityValue]; - } -} - -@end // FlutterSwitchSemanticsObject - -@implementation FlutterCustomAccessibilityAction { -} -@end - -/** - * Represents a semantics object that has children and hence has to be presented to the OS as a - * UIAccessibilityContainer. - * - * The SemanticsObject class cannot implement the UIAccessibilityContainer protocol because an - * object that returns YES for isAccessibilityElement cannot also implement - * UIAccessibilityContainer. - * - * With the help of SemanticsObjectContainer, the hierarchy of semantic objects received from - * the framework, such as: - * - * SemanticsObject1 - * SemanticsObject2 - * SemanticsObject3 - * SemanticsObject4 - * - * is translated into the following hierarchy, which is understood by iOS: - * - * SemanticsObjectContainer1 - * SemanticsObject1 - * SemanticsObjectContainer2 - * SemanticsObject2 - * SemanticsObject3 - * SemanticsObject4 - * - * From Flutter's view of the world (the first tree seen above), we construct iOS's view of the - * world (second tree) as follows: We replace each SemanticsObjects that has children with a - * SemanticsObjectContainer, which has the original SemanticsObject and its children as children. - * - * SemanticsObjects have semantic information attached to them which is interpreted by - * VoiceOver (they return YES for isAccessibilityElement). The SemanticsObjectContainers are just - * there for structure and they don't provide any semantic information to VoiceOver (they return - * NO for isAccessibilityElement). - */ -@interface SemanticsObjectContainer : UIAccessibilityElement -- (instancetype)init __attribute__((unavailable("Use initWithSemanticsObject instead"))); -- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject - bridge:(fml::WeakPtr)bridge - NS_DESIGNATED_INITIALIZER; - -@property(nonatomic, weak) SemanticsObject* semanticsObject; - -@end - -@interface SemanticsObject () -/** Should only be called in conjunction with setting child/parent relationship. */ -- (void)privateSetParent:(SemanticsObject*)parent; -@end - -@implementation SemanticsObject { - fml::scoped_nsobject _container; - NSMutableArray* _children; -} - -#pragma mark - Override base class designated initializers - -// Method declared as unavailable in the interface -- (instancetype)init { - [self release]; - [super doesNotRecognizeSelector:_cmd]; - return nil; -} - -#pragma mark - Designated initializers - -- (instancetype)initWithBridge:(fml::WeakPtr)bridge uid:(int32_t)uid { - FML_DCHECK(bridge) << "bridge must be set"; - FML_DCHECK(uid >= kRootNodeId); - // Initialize with the UIView as the container. - // The UIView will not necessarily be accessibility parent for this object. - // The bridge informs the OS of the actual structure via - // `accessibilityContainer` and `accessibilityElementAtIndex`. - self = [super initWithAccessibilityContainer:bridge->view()]; - - if (self) { - _bridge = bridge; - _uid = uid; - _children = [[NSMutableArray alloc] init]; - } - - return self; -} - -- (void)dealloc { - for (SemanticsObject* child in _children) { - [child privateSetParent:nil]; - } - [_children removeAllObjects]; - [_children release]; - _parent = nil; - _container.get().semanticsObject = nil; - [_platformViewSemanticsContainer release]; - [super dealloc]; -} - -#pragma mark - Semantic object methods - -- (BOOL)isAccessibilityBridgeAlive { - return [self bridge].get() != nil; -} - -- (void)setSemanticsNode:(const flutter::SemanticsNode*)node { - _node = *node; -} - -/** - * Whether calling `setSemanticsNode:` with `node` would cause a layout change. - */ -- (BOOL)nodeWillCauseLayoutChange:(const flutter::SemanticsNode*)node { - return [self node].rect != node->rect || [self node].transform != node->transform; -} - -/** - * Whether calling `setSemanticsNode:` with `node` would cause a scroll event. - */ -- (BOOL)nodeWillCauseScroll:(const flutter::SemanticsNode*)node { - return !isnan([self node].scrollPosition) && !isnan(node->scrollPosition) && - [self node].scrollPosition != node->scrollPosition; -} - -- (BOOL)hasChildren { - if (_node.IsPlatformViewNode()) { - return YES; - } - return [self.children count] != 0; -} - -- (void)privateSetParent:(SemanticsObject*)parent { - _parent = parent; -} - -- (void)setChildren:(NSArray*)children { - for (SemanticsObject* child in _children) { - [child privateSetParent:nil]; - } - [_children release]; - _children = [[NSMutableArray alloc] initWithArray:children]; - for (SemanticsObject* child in _children) { - [child privateSetParent:self]; - } -} - -- (void)replaceChildAtIndex:(NSInteger)index withChild:(SemanticsObject*)child { - SemanticsObject* oldChild = _children[index]; - [oldChild privateSetParent:nil]; - [child privateSetParent:self]; - [_children replaceObjectAtIndex:index withObject:child]; -} - -#pragma mark - UIAccessibility overrides - -- (BOOL)isAccessibilityElement { - if (![self isAccessibilityBridgeAlive]) - return false; - - // Note: hit detection will only apply to elements that report - // -isAccessibilityElement of YES. The framework will continue scanning the - // entire element tree looking for such a hit. - - // We enforce in the framework that no other useful semantics are merged with these nodes. - if ([self node].HasFlag(flutter::SemanticsFlags::kScopesRoute)) - return false; - - // If the only flag(s) set are scrolling related AND - // The only flags set are not kIsHidden OR - // The node doesn't have a label, value, or hint OR - // The only actions set are scrolling related actions. - // - // The kIsHidden flag set with any other flag just means this node is now - // hidden but still is a valid target for a11y focus in the tree, e.g. a list - // item that is currently off screen but the a11y navigation needs to know - // about. - return (([self node].flags & ~flutter::kScrollableSemanticsFlags) != 0 && - [self node].flags != static_cast(flutter::SemanticsFlags::kIsHidden)) || - ![self node].label.empty() || ![self node].value.empty() || ![self node].hint.empty() || - ([self node].actions & ~flutter::kScrollableSemanticsActions) != 0; -} - -- (void)collectRoutes:(NSMutableArray*)edges { - if ([self node].HasFlag(flutter::SemanticsFlags::kScopesRoute)) - [edges addObject:self]; - if ([self hasChildren]) { - for (SemanticsObject* child in self.children) { - [child collectRoutes:edges]; - } - } -} - -- (BOOL)onCustomAccessibilityAction:(FlutterCustomAccessibilityAction*)action { - if (![self node].HasAction(flutter::SemanticsAction::kCustomAction)) - return NO; - int32_t action_id = action.uid; - std::vector args; - args.push_back(3); // type=int32. - args.push_back(action_id); - args.push_back(action_id >> 8); - args.push_back(action_id >> 16); - args.push_back(action_id >> 24); - [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kCustomAction, - std::move(args)); - return YES; -} - -- (NSString*)routeName { - // Returns the first non-null and non-empty semantic label of a child - // with an NamesRoute flag. Otherwise returns nil. - if ([self node].HasFlag(flutter::SemanticsFlags::kNamesRoute)) { - NSString* newName = [self accessibilityLabel]; - if (newName != nil && [newName length] > 0) { - return newName; - } - } - if ([self hasChildren]) { - for (SemanticsObject* child in self.children) { - NSString* newName = [child routeName]; - if (newName != nil && [newName length] > 0) { - return newName; - } - } - } - return nil; -} - -- (NSString*)accessibilityLabel { - if (![self isAccessibilityBridgeAlive]) - return nil; - - if ([self node].label.empty()) - return nil; - return @([self node].label.data()); -} - -- (NSString*)accessibilityHint { - if (![self isAccessibilityBridgeAlive]) - return nil; - - if ([self node].hint.empty()) - return nil; - return @([self node].hint.data()); -} - -- (NSString*)accessibilityValue { - if (![self isAccessibilityBridgeAlive]) - return nil; - - if (![self node].value.empty()) { - return @([self node].value.data()); - } - - // FlutterSwitchSemanticsObject should supercede these conditionals. - if ([self node].HasFlag(flutter::SemanticsFlags::kHasToggledState) || - [self node].HasFlag(flutter::SemanticsFlags::kHasCheckedState)) { - if ([self node].HasFlag(flutter::SemanticsFlags::kIsToggled) || - [self node].HasFlag(flutter::SemanticsFlags::kIsChecked)) { - return @"1"; - } else { - return @"0"; - } - } - - return nil; -} - -- (CGRect)accessibilityFrame { - if (![self isAccessibilityBridgeAlive]) - return CGRectMake(0, 0, 0, 0); - - if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden)) { - return [super accessibilityFrame]; - } - return [self globalRect]; -} - -- (CGRect)globalRect { - SkMatrix44 globalTransform = [self node].transform; - for (SemanticsObject* parent = [self parent]; parent; parent = parent.parent) { - globalTransform = parent.node.transform * globalTransform; - } - - SkPoint quad[4]; - [self node].rect.toQuad(quad); - for (auto& point : quad) { - SkScalar vector[4] = {point.x(), point.y(), 0, 1}; - globalTransform.mapScalars(vector); - point.set(vector[0] / vector[3], vector[1] / vector[3]); - } - SkRect rect; - rect.setBounds(quad, 4); - - // `rect` is in the physical pixel coordinate system. iOS expects the accessibility frame in - // the logical pixel coordinate system. Therefore, we divide by the `scale` (pixel ratio) to - // convert. - CGFloat scale = [[[self bridge]->view() window] screen].scale; - auto result = - CGRectMake(rect.x() / scale, rect.y() / scale, rect.width() / scale, rect.height() / scale); - return UIAccessibilityConvertFrameToScreenCoordinates(result, [self bridge]->view()); -} - -#pragma mark - UIAccessibilityElement protocol - -- (id)accessibilityContainer { - if ([self hasChildren] || [self uid] == kRootNodeId) { - if (_container == nil) - _container.reset([[SemanticsObjectContainer alloc] initWithSemanticsObject:self - bridge:[self bridge]]); - return _container.get(); - } - if ([self parent] == nil) { - // This can happen when we have released the accessibility tree but iOS is - // still holding onto our objects. iOS can take some time before it - // realizes that the tree has changed. - return nil; - } - return [[self parent] accessibilityContainer]; -} - -#pragma mark - UIAccessibilityAction overrides - -- (BOOL)accessibilityActivate { - if (![self isAccessibilityBridgeAlive]) - return NO; - if (![self node].HasAction(flutter::SemanticsAction::kTap)) - return NO; - [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kTap); - return YES; -} - -- (void)accessibilityIncrement { - if (![self isAccessibilityBridgeAlive]) - return; - if ([self node].HasAction(flutter::SemanticsAction::kIncrease)) { - [self node].value = [self node].increasedValue; - [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kIncrease); - } -} - -- (void)accessibilityDecrement { - if (![self isAccessibilityBridgeAlive]) - return; - if ([self node].HasAction(flutter::SemanticsAction::kDecrease)) { - [self node].value = [self node].decreasedValue; - [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDecrease); - } -} - -- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { - if (![self isAccessibilityBridgeAlive]) - return NO; - flutter::SemanticsAction action = GetSemanticsActionForScrollDirection(direction); - if (![self node].HasAction(action)) - return NO; - [self bridge]->DispatchSemanticsAction([self uid], action); - return YES; -} - -- (BOOL)accessibilityPerformEscape { - if (![self isAccessibilityBridgeAlive]) - return NO; - if (![self node].HasAction(flutter::SemanticsAction::kDismiss)) - return NO; - [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kDismiss); - return YES; -} - -#pragma mark UIAccessibilityFocus overrides - -- (void)accessibilityElementDidBecomeFocused { - if (![self isAccessibilityBridgeAlive]) - return; - if ([self node].HasFlag(flutter::SemanticsFlags::kIsHidden)) { - [self bridge]->DispatchSemanticsAction([self uid], flutter::SemanticsAction::kShowOnScreen); - } - if ([self node].HasAction(flutter::SemanticsAction::kDidGainAccessibilityFocus)) { - [self bridge]->DispatchSemanticsAction([self uid], - flutter::SemanticsAction::kDidGainAccessibilityFocus); - } -} - -- (void)accessibilityElementDidLoseFocus { - if (![self isAccessibilityBridgeAlive]) - return; - if ([self node].HasAction(flutter::SemanticsAction::kDidLoseAccessibilityFocus)) { - [self bridge]->DispatchSemanticsAction([self uid], - flutter::SemanticsAction::kDidLoseAccessibilityFocus); - } -} - -@end - -@implementation FlutterSemanticsObject { -} - -#pragma mark - Override base class designated initializers - -// Method declared as unavailable in the interface -- (instancetype)init { - [self release]; - [super doesNotRecognizeSelector:_cmd]; - return nil; -} - -#pragma mark - Designated initializers - -- (instancetype)initWithBridge:(fml::WeakPtr)bridge uid:(int32_t)uid { - self = [super initWithBridge:bridge uid:uid]; - return self; -} - -#pragma mark - UIAccessibility overrides - -- (UIAccessibilityTraits)accessibilityTraits { - UIAccessibilityTraits traits = UIAccessibilityTraitNone; - if ([self node].HasAction(flutter::SemanticsAction::kIncrease) || - [self node].HasAction(flutter::SemanticsAction::kDecrease)) { - traits |= UIAccessibilityTraitAdjustable; - } - // FlutterSwitchSemanticsObject should supercede these conditionals. - if ([self node].HasFlag(flutter::SemanticsFlags::kHasToggledState) || - [self node].HasFlag(flutter::SemanticsFlags::kHasCheckedState)) { - traits |= UIAccessibilityTraitButton; - } - if ([self node].HasFlag(flutter::SemanticsFlags::kIsSelected)) { - traits |= UIAccessibilityTraitSelected; - } - if ([self node].HasFlag(flutter::SemanticsFlags::kIsButton)) { - traits |= UIAccessibilityTraitButton; - } - if ([self node].HasFlag(flutter::SemanticsFlags::kHasEnabledState) && - ![self node].HasFlag(flutter::SemanticsFlags::kIsEnabled)) { - traits |= UIAccessibilityTraitNotEnabled; - } - if ([self node].HasFlag(flutter::SemanticsFlags::kIsHeader)) { - traits |= UIAccessibilityTraitHeader; - } - if ([self node].HasFlag(flutter::SemanticsFlags::kIsImage)) { - traits |= UIAccessibilityTraitImage; - } - if ([self node].HasFlag(flutter::SemanticsFlags::kIsLiveRegion)) { - traits |= UIAccessibilityTraitUpdatesFrequently; - } - if ([self node].HasFlag(flutter::SemanticsFlags::kIsLink)) { - traits |= UIAccessibilityTraitLink; - } - return traits; -} - -@end - -@implementation FlutterPlatformViewSemanticsContainer { - SemanticsObject* _semanticsObject; - UIView* _platformView; -} - -// Method declared as unavailable in the interface -- (instancetype)init { - [self release]; - [super doesNotRecognizeSelector:_cmd]; - return nil; -} - -- (instancetype)initWithSemanticsObject:(SemanticsObject*)object { - FML_CHECK(object); - // Initialize with the UIView as the container. - // The UIView will not necessarily be accessibility parent for this object. - // The bridge informs the OS of the actual structure via - // `accessibilityContainer` and `accessibilityElementAtIndex`. - if (self = [super initWithAccessibilityContainer:object.bridge->view()]) { - _semanticsObject = object; - flutter::FlutterPlatformViewsController* controller = - object.bridge->GetPlatformViewsController(); - if (controller) { - _platformView = [controller->GetPlatformViewByID(object.node.platformViewId) view]; - } - self.accessibilityElements = @[ _semanticsObject, _platformView ]; - } - return self; -} - -- (CGRect)accessibilityFrame { - return _semanticsObject.accessibilityFrame; -} - -- (BOOL)isAccessibilityElement { - return NO; -} - -- (id)accessibilityContainer { - return [_semanticsObject accessibilityContainer]; -} - -- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { - return [_platformView accessibilityScroll:direction]; -} - -@end - -@implementation SemanticsObjectContainer { - SemanticsObject* _semanticsObject; - fml::WeakPtr _bridge; -} - -#pragma mark - initializers - -// Method declared as unavailable in the interface -- (instancetype)init { - [self release]; - [super doesNotRecognizeSelector:_cmd]; - return nil; -} - -- (instancetype)initWithSemanticsObject:(SemanticsObject*)semanticsObject - bridge:(fml::WeakPtr)bridge { - FML_DCHECK(semanticsObject) << "semanticsObject must be set"; - // Initialize with the UIView as the container. - // The UIView will not necessarily be accessibility parent for this object. - // The bridge informs the OS of the actual structure via - // `accessibilityContainer` and `accessibilityElementAtIndex`. - self = [super initWithAccessibilityContainer:bridge->view()]; - - if (self) { - _semanticsObject = semanticsObject; - _bridge = bridge; - } - - return self; -} - -#pragma mark - UIAccessibilityContainer overrides - -- (NSInteger)accessibilityElementCount { - NSInteger count = [[_semanticsObject children] count] + 1; - return count; -} - -- (nullable id)accessibilityElementAtIndex:(NSInteger)index { - if (index < 0 || index >= [self accessibilityElementCount]) - return nil; - if (index == 0) { - return _semanticsObject; - } - - SemanticsObject* child = [_semanticsObject children][index - 1]; - - // Swap the original `SemanticsObject` to a `PlatformViewSemanticsContainer` - if (child.node.IsPlatformViewNode()) { - child.platformViewSemanticsContainer.index = index; - return child.platformViewSemanticsContainer; - } - - if ([child hasChildren]) - return [child accessibilityContainer]; - return child; -} - -- (NSInteger)indexOfAccessibilityElement:(id)element { - if (element == _semanticsObject) - return 0; - - // FlutterPlatformViewSemanticsContainer is always the last element of its parent. - if ([element isKindOfClass:[FlutterPlatformViewSemanticsContainer class]]) { - return ((FlutterPlatformViewSemanticsContainer*)element).index; - } - - NSArray* children = [_semanticsObject children]; - for (size_t i = 0; i < [children count]; i++) { - SemanticsObject* child = children[i]; - if ((![child hasChildren] && child == element) || - ([child hasChildren] && [child accessibilityContainer] == element)) - return i + 1; - } - return NSNotFound; -} - -#pragma mark - UIAccessibilityElement protocol - -- (BOOL)isAccessibilityElement { - return NO; -} - -- (CGRect)accessibilityFrame { - return [_semanticsObject accessibilityFrame]; -} - -- (id)accessibilityContainer { - if (!_bridge) { - return nil; - } - return ([_semanticsObject uid] == kRootNodeId) - ? _bridge->view() - : [[_semanticsObject parent] accessibilityContainer]; -} - -#pragma mark - UIAccessibilityAction overrides - -- (BOOL)accessibilityScroll:(UIAccessibilityScrollDirection)direction { - return [_semanticsObject accessibilityScroll:direction]; -} - -@end - -#pragma mark - AccessibilityBridge impl - namespace flutter { AccessibilityBridge::AccessibilityBridge(UIView* view, diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h new file mode 100644 index 0000000000000..c2546ac7c3a2c --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge_ios.h @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_ACCESSIBILITY_BRIDGE_IOS_H_ +#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_ACCESSIBILITY_BRIDGE_IOS_H_ + +#include + +#include "flutter/lib/ui/semantics/semantics_node.h" + +@class UIView; + +namespace flutter { +class FlutterPlatformViewsController; + +/// Interface that represents an accessibility bridge for iOS. +class AccessibilityBridgeIos { + public: + virtual ~AccessibilityBridgeIos() = default; + virtual UIView* view() const = 0; + virtual UIView* textInputView() = 0; + virtual void DispatchSemanticsAction(int32_t id, flutter::SemanticsAction action) = 0; + virtual void DispatchSemanticsAction(int32_t id, + flutter::SemanticsAction action, + std::vector args) = 0; + virtual FlutterPlatformViewsController* GetPlatformViewsController() const = 0; +}; + +} // namespace flutter + +#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_ACCESSIBILITY_BRIDGE_IOS_H_ diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm index 1c299418928d2..f39f19174d562 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm @@ -177,7 +177,8 @@ @implementation TextInputSemanticsObject { FlutterInactiveTextInput* _inactive_text_input; } -- (instancetype)initWithBridge:(fml::WeakPtr)bridge uid:(int32_t)uid { +- (instancetype)initWithBridge:(fml::WeakPtr)bridge + uid:(int32_t)uid { self = [super initWithBridge:bridge uid:uid]; if (self) { From fdbc21d2b5463bc6191ac085e999d2f0adacca0e Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 4 Apr 2020 01:26:03 -0400 Subject: [PATCH 268/386] Roll src/third_party/dart f144d5fdca56..1e4a56ee7202 (20 commits) (#17510) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index c3cf66e5a8956..212c48422f0c4 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'f144d5fdca56e53b80f858140620982aec7e2eea', + 'dart_revision': '1e4a56ee720218ae82af13a9cf71e038b0884cfc', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index e830ead29b486..a451f99b621fb 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 68c93925c73188171c45b4ad5b310f0c +Signature: 87054d758bd439e5e2fa3d34486c7ed0 UNUSED LICENSES: @@ -7796,11 +7796,9 @@ FILE: ../../../third_party/dart/runtime/vm/visitor.cc FILE: ../../../third_party/dart/samples/ffi/async/async_test.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_async_callback.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_native_port_call.dart -FILE: ../../../third_party/dart/sdk/lib/_http/embedder_config.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart -FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/embedder_config.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/js_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/vm/lib/ffi_struct_patch.dart From 53cf74ea91bc3df1d1aedc305f4d270b1e8874e7 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 4 Apr 2020 05:26:02 -0400 Subject: [PATCH 269/386] Roll fuchsia/sdk/core/mac-amd64 from tKvUB... to BHVYY... (#17512) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 212c48422f0c4..d9c13d12faa55 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'tKvUBSZoYR-8s8xKYPqEnhBACvjbfOfU1Lxx_cP_-XYC' + 'version': 'BHVYYBrA9MXvpujaQy7cwg8GjY_IISnjQFPk6JMhAWEC' } ], 'condition': 'host_os == "mac"', From 260683e567e358205fa5899f69674d9eec1cd804 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 4 Apr 2020 06:56:02 -0400 Subject: [PATCH 270/386] Roll src/third_party/dart 1e4a56ee7202..28eb884d4709 (1 commits) (#17513) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d9c13d12faa55..c30f57623279a 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '1e4a56ee720218ae82af13a9cf71e038b0884cfc', + 'dart_revision': '28eb884d4709e857764de5c2c4aac93a789311de', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 4072be436a2e616b4d71eab900d3af5284a0d5c2 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 4 Apr 2020 07:46:01 -0400 Subject: [PATCH 271/386] Roll fuchsia/sdk/core/linux-amd64 from TWx2R... to hUO_b... (#17514) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index c30f57623279a..859537f5f7c6b 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'TWx2Rkn7JXtD4kl2fw_lug0JHAPYt-lB0TcNFj0UIG0C' + 'version': 'hUO_bpNYvADEov9802MK9LaKAwsxiGMx8RX_qicHvOQC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 5471a3d395809..b13bacee1ea8f 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: ae577c7fe76b9b277b8fc274e52bd1bb +Signature: 61ee89d822a3643c5a211be303cdf1f4 UNUSED LICENSES: From cb6fc305346f24f87b4275a26fedfcfa51a57925 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sat, 4 Apr 2020 13:41:03 -0400 Subject: [PATCH 272/386] Roll src/third_party/dart 28eb884d4709..80ae6ed91d6d (2 commits) (#17515) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 859537f5f7c6b..8ea0595cae9b5 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '28eb884d4709e857764de5c2c4aac93a789311de', + 'dart_revision': '80ae6ed91d6d1d3965fc913d1e5365f8c1ee577d', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index a451f99b621fb..5f65d983ea397 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 87054d758bd439e5e2fa3d34486c7ed0 +Signature: e9de4774ec7951bd1a1095a6b5716426 UNUSED LICENSES: From 2cc6a6d66d507ed7e28ca107f02e9dc8462ef8e0 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 5 Apr 2020 02:31:01 -0400 Subject: [PATCH 273/386] Roll fuchsia/sdk/core/mac-amd64 from BHVYY... to 6CeXq... (#17516) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 8ea0595cae9b5..cfe2de3172ce3 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'BHVYYBrA9MXvpujaQy7cwg8GjY_IISnjQFPk6JMhAWEC' + 'version': '6CeXq5a7KNU21ys5T4gN_iSY7H_LeQNsAfpQKju87N4C' } ], 'condition': 'host_os == "mac"', From ec6e8f4a00b69755106b7aefb5432580960229b9 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 5 Apr 2020 04:26:02 -0400 Subject: [PATCH 274/386] Roll src/third_party/dart 80ae6ed91d6d..a7f1a5e677e5 (1 commits) (#17518) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index cfe2de3172ce3..9be5c4c0bae5e 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '80ae6ed91d6d1d3965fc913d1e5365f8c1ee577d', + 'dart_revision': 'a7f1a5e677e50821d45b9d0d14c470d038c7df7f', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From bd725935ecfa3031e918b3f183adb055d767b1ba Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 5 Apr 2020 07:06:02 -0400 Subject: [PATCH 275/386] Roll fuchsia/sdk/core/linux-amd64 from hUO_b... to Ezm2f... (#17519) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 9be5c4c0bae5e..847bedcdbee91 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'hUO_bpNYvADEov9802MK9LaKAwsxiGMx8RX_qicHvOQC' + 'version': 'Ezm2f4uVNEsbtECXUtJYczOQaLkM_V-ue4cLQKoGQigC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index b13bacee1ea8f..6b34f2a142123 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 61ee89d822a3643c5a211be303cdf1f4 +Signature: 28f48ca1c8f88cfe6fda84f9889514d4 UNUSED LICENSES: @@ -3273,6 +3273,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/night_mode.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/events.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/keyboard.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/modifiers.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/pointer.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.update/update.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.url/url.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.weave/auth.fidl From f1f7d5db59f0c6101eb701253a61787c8bf3cf37 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 5 Apr 2020 15:16:02 -0400 Subject: [PATCH 276/386] Roll src/third_party/dart a7f1a5e677e5..987ad1d96748 (1 commits) (#17520) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 847bedcdbee91..31d1c165741a9 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'a7f1a5e677e50821d45b9d0d14c470d038c7df7f', + 'dart_revision': '987ad1d9674893393daf948938960e4eb1859180', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From 6e4817f09c47d9e4e106c37914b832780ad93b25 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 5 Apr 2020 17:16:02 -0400 Subject: [PATCH 277/386] Roll fuchsia/sdk/core/mac-amd64 from 6CeXq... to LDdBU... (#17521) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 31d1c165741a9..87dc139edbe28 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '6CeXq5a7KNU21ys5T4gN_iSY7H_LeQNsAfpQKju87N4C' + 'version': 'LDdBUxWp_gfIL2aIibEYMKh-qMPj4Xd7V8cs6NVExOsC' } ], 'condition': 'host_os == "mac"', From 9b8dcc7ecffe21c1026508da3c33067cfb3f6e93 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Sun, 5 Apr 2020 21:31:03 -0400 Subject: [PATCH 278/386] Roll src/third_party/dart 987ad1d96748..05103dfe5a0e (2 commits) (#17522) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 87dc139edbe28..670d8b536bb19 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '987ad1d9674893393daf948938960e4eb1859180', + 'dart_revision': '05103dfe5a0e2f354f33c96da7bbf13dc79c3915', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From fd2f0d290e5a09e4b85f1694fabdd598942f8bd8 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 6 Apr 2020 03:16:01 -0400 Subject: [PATCH 279/386] Roll fuchsia/sdk/core/linux-amd64 from Ezm2f... to 3yOjK... (#17525) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 670d8b536bb19..d26362ff74bd5 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'Ezm2f4uVNEsbtECXUtJYczOQaLkM_V-ue4cLQKoGQigC' + 'version': '3yOjKk40jfIT70Xdy4AQhLFZUqv75tVq0hRwuE7WuKUC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 6b34f2a142123..783c451af72c5 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 28f48ca1c8f88cfe6fda84f9889514d4 +Signature: da0285b14c607814ab017e4c18d31717 UNUSED LICENSES: From 03bbd7c8108ce21f0227a17ef6d46e020f8802d3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 6 Apr 2020 03:36:01 -0400 Subject: [PATCH 280/386] Roll src/third_party/dart 05103dfe5a0e..a8251f820b09 (4 commits) (#17526) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index d26362ff74bd5..7d6cc5d0a2ec1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '05103dfe5a0e2f354f33c96da7bbf13dc79c3915', + 'dart_revision': 'a8251f820b0975f07f9868c8468d0b32805857e2', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py From dabda2c66e2d4349324bce9b67f3ed95d8b7b794 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 6 Apr 2020 07:36:02 -0400 Subject: [PATCH 281/386] Roll fuchsia/sdk/core/mac-amd64 from LDdBU... to wZ5qZ... (#17527) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 7d6cc5d0a2ec1..d39e0a6065493 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'LDdBUxWp_gfIL2aIibEYMKh-qMPj4Xd7V8cs6NVExOsC' + 'version': 'wZ5qZ-VayHwkLFBdLl9lmjUt_CtxHjn9-oug0uWYB_MC' } ], 'condition': 'host_os == "mac"', From df257e59c24175e1f7181db7524c6df605e50031 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 6 Apr 2020 12:06:04 -0400 Subject: [PATCH 282/386] Roll src/third_party/dart a8251f820b09..1210d27678a0 (5 commits) (#17528) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d39e0a6065493..becb5218ad683 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'a8251f820b0975f07f9868c8468d0b32805857e2', + 'dart_revision': '1210d27678a05e5e69d82766809d816b7aa91830', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 5f65d983ea397..bc5cce69ce2f1 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: e9de4774ec7951bd1a1095a6b5716426 +Signature: 94fc349da32da798da36c79d18f58784 UNUSED LICENSES: From 0945635661e5a54df7cdff85ea2631e320aa14ff Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 6 Apr 2020 12:11:02 -0400 Subject: [PATCH 283/386] Roll src/third_party/skia cc8a76f3c763..04513752fd6e (22 commits) (#17529) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index becb5218ad683..f84a6c5b49cec 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'cc8a76f3c763b503e3042db807c5cb1e33d6fe10', + 'skia_revision': '04513752fd6e30a9bc4181e7fd901908a432ad0b', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 73911dde0680b..5378801ce747f 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 1d3e051c6ddd101b0625007dc5354da8 +Signature: 8a3301ac1f44f6d24dca6cb80e9896fa UNUSED LICENSES: @@ -3889,6 +3889,8 @@ FILE: ../../../third_party/skia/src/core/SkVM_fwd.h FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.cpp FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.h FILE: ../../../third_party/skia/src/gpu/GrManagedResource.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DBuffer.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCaps.h FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DCommandList.cpp @@ -5607,6 +5609,7 @@ FILE: ../../../third_party/skia/modules/skottie/src/animator/TextKeyframeAnimato FILE: ../../../third_party/skia/modules/skottie/src/animator/Vec2KeyframeAnimator.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.cpp FILE: ../../../third_party/skia/modules/skottie/src/animator/VectorKeyframeAnimator.h +FILE: ../../../third_party/skia/modules/skottie/src/effects/CornerPinEffect.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Ellipse.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/FillStroke.cpp FILE: ../../../third_party/skia/modules/skottie/src/layers/shapelayer/Gradient.cpp From faf44fed5a5913dcbeebd7ead8e3933a5e72a6fc Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Mon, 6 Apr 2020 09:55:42 -0700 Subject: [PATCH 284/386] Improve C++ plugin lifetime handling (#17489) This makes two changes: - Adds a way to register a callback for when a FlutterDesktopPluginRegistrarRef is destroyed, and implements the logic to call it in the Windows and Linux embeddings. - Adds a class to the C++ wrapper that handles making a singleton owning PluginRegistrar wrappers, and destroying them when the underlying reference goes away, to avoid needing that boilerplate code in every plugin's source. Fixes https://github.com/flutter/flutter/issues/53496 --- .../common/cpp/client_wrapper/BUILD.gn | 2 + .../include/flutter/plugin_registrar.h | 43 +++++++++++ .../cpp/client_wrapper/plugin_registrar.cc | 18 ++++- .../plugin_registrar_unittests.cc | 75 ++++++++++++++++--- .../testing/stub_flutter_api.cc | 8 ++ .../client_wrapper/testing/stub_flutter_api.h | 4 + .../cpp/public/flutter_plugin_registrar.h | 9 +++ shell/platform/glfw/flutter_glfw.cc | 14 ++++ shell/platform/windows/flutter_windows.cc | 6 ++ .../platform/windows/win32_flutter_window.cc | 3 + shell/platform/windows/window_state.h | 3 + 11 files changed, 175 insertions(+), 10 deletions(-) diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index f46cd81461ed0..ed0bdf9e87536 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -65,4 +65,6 @@ executable("client_wrapper_unittests") { # https://github.com/flutter/flutter/issues/41414. "//third_party/dart/runtime:libdart_jit", ] + + defines = [ "FLUTTER_DESKTOP_LIBRARY" ] } diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h index 5fbe1b5b6297c..6169d6437c1cf 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ #define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ +#include #include #include #include @@ -69,6 +70,48 @@ class Plugin { virtual ~Plugin() = default; }; +// A singleton to own PluginRegistrars. This is intended for use in plugins, +// where there is no higher-level object to own a PluginRegistrar that can +// own plugin instances and ensure that they live as long as the engine they +// are registered with. +class PluginRegistrarManager { + public: + static PluginRegistrarManager* GetInstance(); + + // Prevent copying. + PluginRegistrarManager(PluginRegistrarManager const&) = delete; + PluginRegistrarManager& operator=(PluginRegistrarManager const&) = delete; + + // Returns a plugin registrar wrapper of type T, which must be a kind of + // PluginRegistrar, creating it if necessary. The returned registrar will + // live as long as the underlying FlutterDesktopPluginRegistrarRef, so + // can be used to own plugin instances. + // + // Calling this multiple times for the same registrar_ref with different + // template types results in undefined behavior. + template + T* GetRegistrar(FlutterDesktopPluginRegistrarRef registrar_ref) { + auto insert_result = + registrars_.emplace(registrar_ref, std::make_unique(registrar_ref)); + auto& registrar_pair = *(insert_result.first); + FlutterDesktopRegistrarSetDestructionHandler(registrar_pair.first, + OnRegistrarDestroyed); + return static_cast(registrar_pair.second.get()); + } + + private: + PluginRegistrarManager(); + + using WrapperMap = std::map>; + + static void OnRegistrarDestroyed(FlutterDesktopPluginRegistrarRef registrar); + + WrapperMap* registrars() { return ®istrars_; } + + WrapperMap registrars_; +}; + } // namespace flutter #endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc index 7ca7c3de73b1c..92584a93ee903 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc @@ -138,7 +138,7 @@ void BinaryMessengerImpl::SetMessageHandler(const std::string& channel, ForwardToHandler, message_handler); } -// PluginRegistrar: +// ===== PluginRegistrar ===== PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar) : registrar_(registrar) { @@ -157,4 +157,20 @@ void PluginRegistrar::EnableInputBlockingForChannel( FlutterDesktopRegistrarEnableInputBlocking(registrar_, channel.c_str()); } +// ===== PluginRegistrarManager ===== + +// static +PluginRegistrarManager* PluginRegistrarManager::GetInstance() { + static PluginRegistrarManager* instance = new PluginRegistrarManager(); + return instance; +} + +PluginRegistrarManager::PluginRegistrarManager() = default; + +// static +void PluginRegistrarManager::OnRegistrarDestroyed( + FlutterDesktopPluginRegistrarRef registrar) { + GetInstance()->registrars()->erase(registrar); +} + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc index 5f1a5b4421f5e..eee4758c21315 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc @@ -33,28 +33,36 @@ class TestApi : public testing::StubFlutterApi { return message_engine_result; } - // Called for FlutterDesktopMessengerSetCallback. void MessengerSetCallback(const char* channel, FlutterDesktopMessageCallback callback, void* user_data) override { - last_callback_set_ = callback; + last_message_callback_set_ = callback; + } + + void RegistrarSetDestructionHandler( + FlutterDesktopOnRegistrarDestroyed callback) override { + last_destruction_callback_set_ = callback; } const uint8_t* last_data_sent() { return last_data_sent_; } - FlutterDesktopMessageCallback last_callback_set() { - return last_callback_set_; + FlutterDesktopMessageCallback last_message_callback_set() { + return last_message_callback_set_; + } + FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set() { + return last_destruction_callback_set_; } private: const uint8_t* last_data_sent_ = nullptr; - FlutterDesktopMessageCallback last_callback_set_ = nullptr; + FlutterDesktopMessageCallback last_message_callback_set_ = nullptr; + FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set_ = nullptr; }; } // namespace // Tests that the registrar returns a messenger that passes Send through to the // C API. -TEST(MethodCallTest, MessengerSend) { +TEST(PluginRegistrarTest, MessengerSend) { testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); @@ -70,7 +78,7 @@ TEST(MethodCallTest, MessengerSend) { // Tests that the registrar returns a messenger that passes callback // registration and unregistration through to the C API. -TEST(MethodCallTest, MessengerSetMessageHandler) { +TEST(PluginRegistrarTest, MessengerSetMessageHandler) { testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); @@ -85,11 +93,60 @@ TEST(MethodCallTest, MessengerSetMessageHandler) { const size_t message_size, BinaryReply reply) {}; messenger->SetMessageHandler(channel_name, std::move(binary_handler)); - EXPECT_NE(test_api->last_callback_set(), nullptr); + EXPECT_NE(test_api->last_message_callback_set(), nullptr); // Unregister. messenger->SetMessageHandler(channel_name, nullptr); - EXPECT_EQ(test_api->last_callback_set(), nullptr); + EXPECT_EQ(test_api->last_message_callback_set(), nullptr); +} + +// Tests that the registrar manager returns the same instance when getting +// the wrapper for the same reference. +TEST(PluginRegistrarTest, ManagerSameInstance) { + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + + auto dummy_registrar_handle = + reinterpret_cast(1); + + PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); + EXPECT_EQ(manager->GetRegistrar(dummy_registrar_handle), + manager->GetRegistrar(dummy_registrar_handle)); +} + +// Tests that the registrar manager returns different objects for different +// references. +TEST(PluginRegistrarTest, ManagerDifferentInstances) { + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + + auto dummy_registrar_handle_a = + reinterpret_cast(1); + auto dummy_registrar_handle_b = + reinterpret_cast(2); + + PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); + EXPECT_NE(manager->GetRegistrar(dummy_registrar_handle_a), + manager->GetRegistrar(dummy_registrar_handle_b)); +} + +// Tests that the registrar manager deletes wrappers when the underlying +// reference is destroyed. +TEST(PluginRegistrarTest, ManagerRemovesOnDestruction) { + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + + auto dummy_registrar_handle = + reinterpret_cast(1); + PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); + auto* first_wrapper = + manager->GetRegistrar(dummy_registrar_handle); + + // Simulate destruction of the reference. + EXPECT_NE(test_api->last_destruction_callback_set(), nullptr); + test_api->last_destruction_callback_set()(dummy_registrar_handle); + + // Requesting the wrapper should now create a new object. + EXPECT_NE(manager->GetRegistrar(dummy_registrar_handle), + first_wrapper); } } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc index 4bd6ddce01a39..580df48645aa9 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc @@ -44,6 +44,14 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return reinterpret_cast(1); } +void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback) { + if (s_stub_implementation) { + s_stub_implementation->RegistrarSetDestructionHandler(callback); + } +} + void FlutterDesktopRegistrarEnableInputBlocking( FlutterDesktopPluginRegistrarRef registrar, const char* channel) { diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h index c5f9dbbb2a2a5..284d15e974947 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h @@ -34,6 +34,10 @@ class StubFlutterApi { virtual ~StubFlutterApi() {} + // Called for FlutterDesktopRegistrarSetDestructionHandler. + virtual void RegistrarSetDestructionHandler( + FlutterDesktopOnRegistrarDestroyed callback) {} + // Called for FlutterDesktopRegistrarEnableInputBlocking. virtual void RegistrarEnableInputBlocking(const char* channel) {} diff --git a/shell/platform/common/cpp/public/flutter_plugin_registrar.h b/shell/platform/common/cpp/public/flutter_plugin_registrar.h index 1caa3cee1f9e4..95f0abf139608 100644 --- a/shell/platform/common/cpp/public/flutter_plugin_registrar.h +++ b/shell/platform/common/cpp/public/flutter_plugin_registrar.h @@ -18,10 +18,19 @@ extern "C" { // Opaque reference to a plugin registrar. typedef struct FlutterDesktopPluginRegistrar* FlutterDesktopPluginRegistrarRef; +// Function pointer type for registrar destruction callback. +typedef void (*FlutterDesktopOnRegistrarDestroyed)( + FlutterDesktopPluginRegistrarRef); + // Returns the engine messenger associated with this registrar. FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(FlutterDesktopPluginRegistrarRef registrar); +// Registers a callback to be called when the plugin registrar is destroyed. +FLUTTER_EXPORT void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback); + // Enables input blocking on the given channel. // // If set, then the Flutter window will disable input callbacks diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index fa359076c71f6..74a5be634694e 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -118,6 +118,9 @@ struct FlutterDesktopPluginRegistrar { // The handle for the window associated with this registrar. FlutterDesktopWindow* window; + + // Callback to be called on registrar destruction. + FlutterDesktopOnRegistrarDestroyed destruction_handler; }; // State associated with the messenger used to communicate with the engine. @@ -682,6 +685,11 @@ FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow( } void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) { + FlutterDesktopPluginRegistrarRef registrar = + controller->plugin_registrar.get(); + if (registrar->destruction_handler) { + registrar->destruction_handler(registrar); + } FlutterEngineShutdown(controller->engine); delete controller; } @@ -832,6 +840,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return registrar->messenger.get(); } +void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback) { + registrar->destruction_handler = callback; +} + FlutterDesktopWindowRef FlutterDesktopRegistrarGetWindow( FlutterDesktopPluginRegistrarRef registrar) { return registrar->window; diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 25da4374b74bc..c53b055fffeb9 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -248,6 +248,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return registrar->messenger.get(); } +void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback) { + registrar->destruction_handler = callback; +} + FlutterDesktopViewRef FlutterDesktopRegistrarGetView( FlutterDesktopPluginRegistrarRef registrar) { return registrar->window; diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index c0c786799285d..cf69618835373 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -15,6 +15,9 @@ Win32FlutterWindow::Win32FlutterWindow(int width, int height) { Win32FlutterWindow::~Win32FlutterWindow() { DestroyRenderSurface(); + if (plugin_registrar_ && plugin_registrar_->destruction_handler) { + plugin_registrar_->destruction_handler(plugin_registrar_.get()); + } } FlutterDesktopViewControllerRef Win32FlutterWindow::CreateWin32FlutterWindow( diff --git a/shell/platform/windows/window_state.h b/shell/platform/windows/window_state.h index 086f6aebd450f..6f0451d7ec940 100644 --- a/shell/platform/windows/window_state.h +++ b/shell/platform/windows/window_state.h @@ -55,6 +55,9 @@ struct FlutterDesktopPluginRegistrar { // The handle for the window associated with this registrar. FlutterDesktopView* window; + + // Callback to be called on registrar destruction. + FlutterDesktopOnRegistrarDestroyed destruction_handler; }; // State associated with the messenger used to communicate with the engine. From 4b69cf2c49db43ec89bb5004d1f4b767194c4fea Mon Sep 17 00:00:00 2001 From: Joshua Seaton Date: Mon, 6 Apr 2020 10:04:54 -0700 Subject: [PATCH 285/386] [tools][fuchsia] Do not tar debug symbol CIPD uploads (#17506) Fuchsia is the main consumer of these CIPD packages and - to simplify its infrastructure - it is migrating its SDK partners over to producing a CIPD package containing a flat .build-id directory. This change also updates the CIPD package so that the .build-id directory is placed at the root. Bug: fxbug.dev/41443 --- .../fuchsia/merge_and_upload_debug_symbols.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tools/fuchsia/merge_and_upload_debug_symbols.py b/tools/fuchsia/merge_and_upload_debug_symbols.py index 3bf5d852ca972..56de7a910efc0 100755 --- a/tools/fuchsia/merge_and_upload_debug_symbols.py +++ b/tools/fuchsia/merge_and_upload_debug_symbols.py @@ -73,16 +73,22 @@ def ProcessCIPDPackage(upload, cipd_yaml, engine_version, out_dir, target_arch): if tries == num_tries - 1: raise -def CreateTarFile(folder_path, base_dir): - archive_name = os.path.basename(folder_path) - tar_file_path = os.path.join(base_dir, archive_name + '.tar.bz2') - with tarfile.open(tar_file_path, "w:bz2") as archive: - for root, dirs, _ in os.walk(folder_path): - for dir_name in dirs: - dir_path = os.path.join(root, dir_name) - archive.add(dir_path, arcname=dir_name) - return tar_file_path - +# Recursively hardlinks contents from one directory to another, +# skipping over collisions. +def HardlinkContents(dirA, dirB): + for src_dir, _, filenames in os.walk(dirA): + for filename in filenames: + src = os.path.join(src_dir, filename) + dest_dir = os.path.join(dirB, os.path.relpath(src_dir, dirA)) + os.makedirs(dest_dir, exist_ok=True) + dest = os.path.join(dest_dir, filename) + if os.path.exists(dest): + # The last two path components provide a content address for a .build-id entry. + tokens = os.path.split(dest) + name = os.path.join(tokens[-2], tokens[-1]) + print('%s already exists in destination; skipping linking' % name) + continue + os.link(src, dest) def main(): parser = argparse.ArgumentParser() @@ -114,18 +120,17 @@ def main(): for symbol_dir in symbol_dirs: assert os.path.exists(symbol_dir) and os.path.isdir(symbol_dir) - arch = args.target_arch - out_dir = os.path.join(args.out_dir, - 'flutter-fuchsia-debug-symbols-%s' % arch) + out_dir = args.out_dir + if os.path.exists(out_dir): print 'Directory: %s is not empty, deleting it.' % out_dir shutil.rmtree(out_dir) os.makedirs(out_dir) for symbol_dir in symbol_dirs: - archive_path = CreateTarFile(symbol_dir, out_dir) - print('Created archive: ' + archive_path) + HardlinkContents(symbol_dir, out_dir) + arch = args.target_arch cipd_def = WriteCIPDDefinition(arch, out_dir) ProcessCIPDPackage(args.upload, cipd_def, args.engine_version, out_dir, arch) return 0 From 19a7fbf02f492842ecfdf711df5e05a9d2019137 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 6 Apr 2020 13:23:00 -0700 Subject: [PATCH 286/386] Fixed nullability in plugin header and overridden type mismatch error. (#17532) --- shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h | 2 +- .../darwin/ios/framework/Source/FlutterAppDelegate.mm | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 81d03d7ea6ac7..06d20498a0763 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -398,7 +398,7 @@ typedef enum { * * @param pluginKey The unique key identifying the plugin. */ -- (NSObject*)registrarForPlugin:(NSString*)pluginKey; +- (nullable NSObject*)registrarForPlugin:(NSString*)pluginKey; /** * Returns whether the specified plugin has been registered. * diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 42a4081cca13c..204261cf2b109 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -8,6 +8,8 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h" +#pragma GCC diagnostic error "-Woverriding-method-mismatch" + static NSString* kUIBackgroundMode = @"UIBackgroundModes"; static NSString* kRemoteNotificationCapabitiliy = @"remote-notification"; static NSString* kBackgroundFetchCapatibility = @"fetch"; @@ -201,7 +203,7 @@ - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { #pragma mark - Selectors handling -- (void)addApplicationLifeCycleDelegate:(NSObject*)delegate { +- (void)addApplicationLifeCycleDelegate:(NSObject*)delegate { [_lifeCycleDelegate addDelegate:delegate]; } From ab434c5540f5e8e873289a9914a8ad47cd3c9580 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Mon, 6 Apr 2020 14:15:40 -0700 Subject: [PATCH 287/386] Revert "[tools][fuchsia] Do not tar debug symbol CIPD uploads (#17506)" (#17537) This reverts commit 4b69cf2c49db43ec89bb5004d1f4b767194c4fea. --- .../fuchsia/merge_and_upload_debug_symbols.py | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/tools/fuchsia/merge_and_upload_debug_symbols.py b/tools/fuchsia/merge_and_upload_debug_symbols.py index 56de7a910efc0..3bf5d852ca972 100755 --- a/tools/fuchsia/merge_and_upload_debug_symbols.py +++ b/tools/fuchsia/merge_and_upload_debug_symbols.py @@ -73,22 +73,16 @@ def ProcessCIPDPackage(upload, cipd_yaml, engine_version, out_dir, target_arch): if tries == num_tries - 1: raise -# Recursively hardlinks contents from one directory to another, -# skipping over collisions. -def HardlinkContents(dirA, dirB): - for src_dir, _, filenames in os.walk(dirA): - for filename in filenames: - src = os.path.join(src_dir, filename) - dest_dir = os.path.join(dirB, os.path.relpath(src_dir, dirA)) - os.makedirs(dest_dir, exist_ok=True) - dest = os.path.join(dest_dir, filename) - if os.path.exists(dest): - # The last two path components provide a content address for a .build-id entry. - tokens = os.path.split(dest) - name = os.path.join(tokens[-2], tokens[-1]) - print('%s already exists in destination; skipping linking' % name) - continue - os.link(src, dest) +def CreateTarFile(folder_path, base_dir): + archive_name = os.path.basename(folder_path) + tar_file_path = os.path.join(base_dir, archive_name + '.tar.bz2') + with tarfile.open(tar_file_path, "w:bz2") as archive: + for root, dirs, _ in os.walk(folder_path): + for dir_name in dirs: + dir_path = os.path.join(root, dir_name) + archive.add(dir_path, arcname=dir_name) + return tar_file_path + def main(): parser = argparse.ArgumentParser() @@ -120,17 +114,18 @@ def main(): for symbol_dir in symbol_dirs: assert os.path.exists(symbol_dir) and os.path.isdir(symbol_dir) - out_dir = args.out_dir - + arch = args.target_arch + out_dir = os.path.join(args.out_dir, + 'flutter-fuchsia-debug-symbols-%s' % arch) if os.path.exists(out_dir): print 'Directory: %s is not empty, deleting it.' % out_dir shutil.rmtree(out_dir) os.makedirs(out_dir) for symbol_dir in symbol_dirs: - HardlinkContents(symbol_dir, out_dir) + archive_path = CreateTarFile(symbol_dir, out_dir) + print('Created archive: ' + archive_path) - arch = args.target_arch cipd_def = WriteCIPDDefinition(arch, out_dir) ProcessCIPDPackage(args.upload, cipd_def, args.engine_version, out_dir, arch) return 0 From ba615d52691bc83d0bda014de56946ed1d87cd2c Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Mon, 6 Apr 2020 15:02:47 -0700 Subject: [PATCH 288/386] Added missing declaration (only showed up in g3 builds). (#17535) --- shell/platform/darwin/ios/framework/Source/SemanticsObject.h | 2 ++ .../darwin/ios/framework/Source/accessibility_bridge.mm | 2 ++ 2 files changed, 4 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObject.h b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h index 575b978ab2a32..ba7c4689be463 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObject.h +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObject.h @@ -14,6 +14,7 @@ constexpr int32_t kRootNodeId = 0; +@class FlutterCustomAccessibilityAction; @class FlutterPlatformViewSemanticsContainer; /** @@ -92,6 +93,7 @@ constexpr int32_t kRootNodeId = 0; - (BOOL)nodeWillCauseScroll:(const flutter::SemanticsNode*)node; - (void)collectRoutes:(NSMutableArray*)edges; - (NSString*)routeName; +- (BOOL)onCustomAccessibilityAction:(FlutterCustomAccessibilityAction*)action; @end diff --git a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm index 2957489fb42fa..c9c147bdacc55 100644 --- a/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm +++ b/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm @@ -7,6 +7,8 @@ #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" +#pragma GCC diagnostic error "-Wundeclared-selector" + FLUTTER_ASSERT_NOT_ARC namespace flutter { From bd760768c8af349711cd9be08d1cb003a3d4c5d7 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Mon, 6 Apr 2020 15:06:03 -0700 Subject: [PATCH 289/386] [web] Detect when the mouseup occurs outside of window (#17495) --- .../lib/src/engine/pointer_binding.dart | 50 +++++++++++---- .../test/engine/pointer_binding_test.dart | 61 +++++++++++++++++++ 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index d88e1d4074d84..c4ed88afe2793 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -166,7 +166,7 @@ abstract class _BaseAdapter { /// Remove all active event listeners. void clearListeners() { _listeners.forEach((String eventName, html.EventListener listener) { - glassPaneElement.removeEventListener(eventName, listener, true); + html.window.removeEventListener(eventName, listener, true); }); // For native listener, we will need to remove it through native javascript // api. @@ -183,8 +183,23 @@ abstract class _BaseAdapter { _nativeListeners.clear(); } - void addEventListener(String eventName, html.EventListener handler) { + /// Adds a listener to the given [eventName]. + /// + /// The event listener is attached to [html.window] but only events that have + /// [glassPaneElement] as a target will be let through by default. + /// + /// If [acceptOutsideGlasspane] is set to true, events outside of the + /// glasspane will also invoke the [handler]. + void addEventListener( + String eventName, + html.EventListener handler, { + bool acceptOutsideGlasspane = false, + }) { final html.EventListener loggedHandler = (html.Event event) { + if (!acceptOutsideGlasspane && event.target != glassPaneElement) { + return; + } + if (_debugLogPointerEvents) { print(event.type); } @@ -196,8 +211,11 @@ abstract class _BaseAdapter { } }; _listeners[eventName] = loggedHandler; - glassPaneElement - .addEventListener(eventName, loggedHandler, true); + // We have to attach the event listener on the window instead of the + // glasspane element. That's because "up" events that occur outside the + // browser are only reported on window, not on DOM elements. + // See: https://github.com/flutter/flutter/issues/52827 + html.window.addEventListener(eventName, loggedHandler, true); } /// Converts a floating number timestamp (in milliseconds) to a [Duration] by @@ -412,11 +430,15 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { } } - void _addPointerEventListener(String eventName, _PointerEventListener handler) { + void _addPointerEventListener( + String eventName, + _PointerEventListener handler, { + bool acceptOutsideGlasspane = false, + }) { addEventListener(eventName, (html.Event event) { final html.PointerEvent pointerEvent = event; return handler(pointerEvent); - }); + }, acceptOutsideGlasspane: acceptOutsideGlasspane); } @override @@ -444,7 +466,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { _convertEventsToPointerData(data: pointerData, event: event, details: details); } _callback(pointerData); - }); + }, acceptOutsideGlasspane: true); _addPointerEventListener('pointerup', (html.PointerEvent event) { final int device = event.pointerId; @@ -455,7 +477,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { _convertEventsToPointerData(data: pointerData, event: event, details: details); } _callback(pointerData); - }); + }, acceptOutsideGlasspane: true); // A browser fires cancel event if it concludes the pointer will no longer // be able to generate events (example: device is deactivated) @@ -706,11 +728,15 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { final _ButtonSanitizer _sanitizer = _ButtonSanitizer(); - void _addMouseEventListener(String eventName, _MouseEventListener handler) { + void _addMouseEventListener( + String eventName, + _MouseEventListener handler, { + bool acceptOutsideGlasspane = false, + }) { addEventListener(eventName, (html.Event event) { final html.MouseEvent mouseEvent = event; return handler(mouseEvent); - }); + }, acceptOutsideGlasspane: acceptOutsideGlasspane); } @override @@ -731,7 +757,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { final _SanitizedDetails sanitizedDetails = _sanitizer.sanitizeMoveEvent(buttons: event.buttons); _convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails); _callback(pointerData); - }); + }, acceptOutsideGlasspane: true); _addMouseEventListener('mouseup', (html.MouseEvent event) { final List pointerData = []; @@ -741,7 +767,7 @@ class _MouseAdapter extends _BaseAdapter with _WheelEventListenerMixin { _sanitizer.sanitizeMoveEvent(buttons: event.buttons); _convertEventsToPointerData(data: pointerData, event: event, details: sanitizedDetails); _callback(pointerData); - }); + }, acceptOutsideGlasspane: true); _addWheelEventListener((html.Event event) { assert(event is html.WheelEvent); diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index caa345b238534..dbe030d4910bf 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -1446,6 +1446,67 @@ void main() { }, ); + _testEach<_ButtonedEventMixin>( + [_PointerEventContext(), _MouseEventContext()], + 'correctly detects up event outside of glasspane', + (_ButtonedEventMixin context) { + PointerBinding.instance.debugOverrideDetector(context); + // This can happen when the up event occurs while the mouse is outside the + // browser window. + + List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + // Press and drag around. + glassPane.dispatchEvent(context.primaryDown( + clientX: 10.0, + clientY: 10.0, + )); + glassPane.dispatchEvent(context.primaryMove( + clientX: 12.0, + clientY: 10.0, + )); + glassPane.dispatchEvent(context.primaryMove( + clientX: 15.0, + clientY: 10.0, + )); + glassPane.dispatchEvent(context.primaryMove( + clientX: 20.0, + clientY: 10.0, + )); + packets.clear(); + + // Move outside the glasspane. + html.window.dispatchEvent(context.primaryMove( + clientX: 900.0, + clientY: 1900.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].physicalX, equals(900.0)); + expect(packets[0].data[0].physicalY, equals(1900.0)); + packets.clear(); + + // Release outside the glasspane. + html.window.dispatchEvent(context.primaryUp( + clientX: 1000.0, + clientY: 2000.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].physicalX, equals(1000.0)); + expect(packets[0].data[0].physicalY, equals(2000.0)); + expect(packets[0].data[1].change, equals(ui.PointerChange.up)); + expect(packets[0].data[1].physicalX, equals(1000.0)); + expect(packets[0].data[1].physicalY, equals(2000.0)); + packets.clear(); + }, + ); + // MULTIPOINTER ADAPTERS _testEach<_MultiPointerEventMixin>( From 2fab00eb835206d671d9bc29a84b432c8e969c1c Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Mon, 6 Apr 2020 15:06:07 -0700 Subject: [PATCH 290/386] Fix AlertDialogs built by platform views (#17511) --- .../platform/SingleViewPresentation.java | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java index 63bf92555c410..993f88efb2a8c 100644 --- a/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java +++ b/shell/platform/android/io/flutter/plugin/platform/SingleViewPresentation.java @@ -9,6 +9,7 @@ import static android.view.View.OnFocusChangeListener; import android.annotation.TargetApi; +import android.app.AlertDialog; import android.app.Presentation; import android.content.Context; import android.content.ContextWrapper; @@ -102,6 +103,9 @@ static class PresentationState { private boolean startFocused = false; + // The context for the application window that hosts FlutterView. + private final Context outerContext; + /** * Creates a presentation that will use the view factory to create a new platform view in the * presentation's onCreate, and attach it. @@ -120,6 +124,7 @@ public SingleViewPresentation( this.viewId = viewId; this.createParams = createParams; this.focusChangeListener = focusChangeListener; + this.outerContext = outerContext; state = new PresentationState(); getWindow() .setFlags( @@ -147,6 +152,7 @@ public SingleViewPresentation( viewFactory = null; this.state = state; this.focusChangeListener = focusChangeListener; + this.outerContext = outerContext; getWindow() .setFlags( WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, @@ -173,7 +179,8 @@ protected void onCreate(Bundle savedInstanceState) { // Our base mContext has already been wrapped with an IMM cache at instantiation time, but // we want to wrap it again here to also return state.windowManagerHandler. - Context context = new PresentationContext(getContext(), state.windowManagerHandler); + Context context = + new PresentationContext(getContext(), state.windowManagerHandler, outerContext); if (state.platformView == null) { state.platformView = viewFactory.create(context, viewId, createParams); @@ -304,15 +311,34 @@ public Context createDisplayContext(Display display) { private static class PresentationContext extends ContextWrapper { private @NonNull final WindowManagerHandler windowManagerHandler; private @Nullable WindowManager windowManager; + private final Context flutterAppWindowContext; - PresentationContext(Context base, @NonNull WindowManagerHandler windowManagerHandler) { + PresentationContext( + Context base, + @NonNull WindowManagerHandler windowManagerHandler, + Context flutterAppWindowContext) { super(base); this.windowManagerHandler = windowManagerHandler; + this.flutterAppWindowContext = flutterAppWindowContext; } @Override public Object getSystemService(String name) { if (WINDOW_SERVICE.equals(name)) { + if (isCalledFromAlertDialog()) { + // Alert dialogs are showing on top of the entire application and should not be limited to + // the virtual + // display. If we detect that an android.app.AlertDialog constructor is what's fetching + // the window manager + // we return the one for the application's window. + // + // Note that if we don't do this AlertDialog will throw a ClassCastException as down the + // line it tries + // to case this instance to a WindowManagerImpl which the object returned by + // getWindowManager is not + // a subclass of. + return flutterAppWindowContext.getSystemService(name); + } return getWindowManager(); } return super.getSystemService(name); @@ -324,6 +350,17 @@ private WindowManager getWindowManager() { } return windowManager; } + + private boolean isCalledFromAlertDialog() { + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + for (int i = 0; i < stackTraceElements.length && i < 11; i++) { + if (stackTraceElements[i].getClassName().equals(AlertDialog.class.getCanonicalName()) + && stackTraceElements[i].getMethodName().equals("")) { + return true; + } + } + return false; + } } /* From 49891e065313d26bbc2ddc9805fb7ed27edae174 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 6 Apr 2020 18:11:03 -0400 Subject: [PATCH 291/386] Roll src/third_party/skia 04513752fd6e..3ef77ddf9ec4 (3 commits) (#17530) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f84a6c5b49cec..396c07f6aa058 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '04513752fd6e30a9bc4181e7fd901908a432ad0b', + 'skia_revision': '3ef77ddf9ec435d781cf65f40b08df3b86b414c6', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 5378801ce747f..2eaf088cab5e6 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 8a3301ac1f44f6d24dca6cb80e9896fa +Signature: 5d6c2ce77840d4576c5353388581ed1c UNUSED LICENSES: From 47f17a9ec33d387899dfcd991cff604523836be1 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Mon, 6 Apr 2020 18:16:03 -0400 Subject: [PATCH 292/386] Roll src/third_party/dart 1210d27678a0..275a76f2fcd8 (4 commits) (#17531) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 396c07f6aa058..6539c47f634a1 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '1210d27678a05e5e69d82766809d816b7aa91830', + 'dart_revision': '275a76f2fcd879042cdca1d8d9a6f2c1dad4b4f9', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index bc5cce69ce2f1..35b09e305869a 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 94fc349da32da798da36c79d18f58784 +Signature: 1f4f76e288d7a69844dd6193c60b2cb3 UNUSED LICENSES: From d1c90b4284282f9745ecd7c65aa72c612df671c1 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Tue, 7 Apr 2020 10:03:49 -0700 Subject: [PATCH 293/386] Added errors to match g3 builds and simple errors (#17536) "unrecognized-selector" and "overriding-method-mismatch" added as errors. --- common/config.gni | 8 ++++++++ fml/BUILD.gn | 3 +++ shell/platform/darwin/BUILD.gn | 3 +++ shell/platform/darwin/common/BUILD.gn | 7 +++++++ .../darwin/common/framework/Source/FlutterCodecs.mm | 9 ++++++--- shell/platform/darwin/ios/BUILD.gn | 3 +++ .../darwin/ios/framework/Source/FlutterAppDelegate.mm | 2 -- 7 files changed, 30 insertions(+), 5 deletions(-) diff --git a/common/config.gni b/common/config.gni index a3eb20b19a5ae..a9274612223cd 100644 --- a/common/config.gni +++ b/common/config.gni @@ -68,3 +68,11 @@ if (is_fuchsia) { if ((is_ios || is_mac) && defined(enable_bitcode)) { flutter_enable_bitcode = enable_bitcode } + +if (is_ios || is_mac) { + flutter_cflags_objc = [ + "-Werror=overriding-method-mismatch", + "-Werror=undeclared-selector", + ] + flutter_cflags_objcc = flutter_cflags_objc +} diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 9b5626e787b1c..f72ac64bc6a6f 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -120,6 +120,9 @@ source_set("fml") { } if (is_ios || is_mac) { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources += [ "platform/darwin/cf_utils.cc", "platform/darwin/cf_utils.h", diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index f5271ca366e60..bfc29dfe79d76 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -20,6 +20,9 @@ group("darwin") { } source_set("flutter_channels") { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "common/buffer_conversions.h", "common/buffer_conversions.mm", diff --git a/shell/platform/darwin/common/BUILD.gn b/shell/platform/darwin/common/BUILD.gn index 8c5663e84c415..45a9e3092b2bc 100644 --- a/shell/platform/darwin/common/BUILD.gn +++ b/shell/platform/darwin/common/BUILD.gn @@ -2,9 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//flutter/common/config.gni") import("framework_shared.gni") source_set("common") { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "buffer_conversions.h", "buffer_conversions.mm", @@ -34,6 +38,9 @@ config("framework_relative_headers") { # Framework code shared between iOS and macOS. source_set("framework_shared") { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "framework/Source/FlutterChannels.mm", "framework/Source/FlutterCodecs.mm", diff --git a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm index b06dab7b85577..6e9b60cbd7359 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm @@ -13,7 +13,8 @@ + (instancetype)sharedInstance { return _sharedInstance; } -- (NSData*)encode:(NSData*)message { +- (NSData*)encode:(id)message { + NSAssert([message isKindOfClass:[NSData class]], @""); return message; } @@ -31,10 +32,12 @@ + (instancetype)sharedInstance { return _sharedInstance; } -- (NSData*)encode:(NSString*)message { +- (NSData*)encode:(id)message { + NSAssert([message isKindOfClass:[NSString class]], @""); + NSString* stringMessage = message; if (message == nil) return nil; - const char* utf8 = message.UTF8String; + const char* utf8 = stringMessage.UTF8String; return [NSData dataWithBytes:utf8 length:strlen(utf8)]; } diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index c98e2b31bf321..d9be6e2a9f11e 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -46,6 +46,9 @@ shared_library("create_flutter_framework_dylib") { public = _flutter_framework_headers + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "framework/Source/FlutterAppDelegate.mm", "framework/Source/FlutterBinaryMessengerRelay.mm", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 204261cf2b109..3a6d113b5e48b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -8,8 +8,6 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h" -#pragma GCC diagnostic error "-Woverriding-method-mismatch" - static NSString* kUIBackgroundMode = @"UIBackgroundModes"; static NSString* kRemoteNotificationCapabitiliy = @"remote-notification"; static NSString* kBackgroundFetchCapatibility = @"fetch"; From 098761f76db7693d414b1fbe07e86e4be1272400 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 7 Apr 2020 10:22:55 -0700 Subject: [PATCH 294/386] Revert "Improve C++ plugin lifetime handling (#17489)" (#17563) Seems to have triggered flaky failures on the Windows bot since landing. Example failure: [ RUN ] PluginRegistrarTest.ManagerRemovesOnDestruction c:\b\s\w\ir\cache\builder\src\flutter\shell\platform\common\cpp\client_wrapper\plugin_registrar_unittests.cc(149): error: Expected: (manager->GetRegistrar(dummy_registrar_handle)) != (first_wrapper), actual: 000002400A90E3D0 vs 000002400A90E3D0 This reverts commit faf44fed5a5913dcbeebd7ead8e3933a5e72a6fc. --- .../common/cpp/client_wrapper/BUILD.gn | 2 - .../include/flutter/plugin_registrar.h | 43 ----------- .../cpp/client_wrapper/plugin_registrar.cc | 18 +---- .../plugin_registrar_unittests.cc | 75 +++---------------- .../testing/stub_flutter_api.cc | 8 -- .../client_wrapper/testing/stub_flutter_api.h | 4 - .../cpp/public/flutter_plugin_registrar.h | 9 --- shell/platform/glfw/flutter_glfw.cc | 14 ---- shell/platform/windows/flutter_windows.cc | 6 -- .../platform/windows/win32_flutter_window.cc | 3 - shell/platform/windows/window_state.h | 3 - 11 files changed, 10 insertions(+), 175 deletions(-) diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index ed0bdf9e87536..f46cd81461ed0 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -65,6 +65,4 @@ executable("client_wrapper_unittests") { # https://github.com/flutter/flutter/issues/41414. "//third_party/dart/runtime:libdart_jit", ] - - defines = [ "FLUTTER_DESKTOP_LIBRARY" ] } diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h index 6169d6437c1cf..5fbe1b5b6297c 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ #define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ -#include #include #include #include @@ -70,48 +69,6 @@ class Plugin { virtual ~Plugin() = default; }; -// A singleton to own PluginRegistrars. This is intended for use in plugins, -// where there is no higher-level object to own a PluginRegistrar that can -// own plugin instances and ensure that they live as long as the engine they -// are registered with. -class PluginRegistrarManager { - public: - static PluginRegistrarManager* GetInstance(); - - // Prevent copying. - PluginRegistrarManager(PluginRegistrarManager const&) = delete; - PluginRegistrarManager& operator=(PluginRegistrarManager const&) = delete; - - // Returns a plugin registrar wrapper of type T, which must be a kind of - // PluginRegistrar, creating it if necessary. The returned registrar will - // live as long as the underlying FlutterDesktopPluginRegistrarRef, so - // can be used to own plugin instances. - // - // Calling this multiple times for the same registrar_ref with different - // template types results in undefined behavior. - template - T* GetRegistrar(FlutterDesktopPluginRegistrarRef registrar_ref) { - auto insert_result = - registrars_.emplace(registrar_ref, std::make_unique(registrar_ref)); - auto& registrar_pair = *(insert_result.first); - FlutterDesktopRegistrarSetDestructionHandler(registrar_pair.first, - OnRegistrarDestroyed); - return static_cast(registrar_pair.second.get()); - } - - private: - PluginRegistrarManager(); - - using WrapperMap = std::map>; - - static void OnRegistrarDestroyed(FlutterDesktopPluginRegistrarRef registrar); - - WrapperMap* registrars() { return ®istrars_; } - - WrapperMap registrars_; -}; - } // namespace flutter #endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc index 92584a93ee903..7ca7c3de73b1c 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc @@ -138,7 +138,7 @@ void BinaryMessengerImpl::SetMessageHandler(const std::string& channel, ForwardToHandler, message_handler); } -// ===== PluginRegistrar ===== +// PluginRegistrar: PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar) : registrar_(registrar) { @@ -157,20 +157,4 @@ void PluginRegistrar::EnableInputBlockingForChannel( FlutterDesktopRegistrarEnableInputBlocking(registrar_, channel.c_str()); } -// ===== PluginRegistrarManager ===== - -// static -PluginRegistrarManager* PluginRegistrarManager::GetInstance() { - static PluginRegistrarManager* instance = new PluginRegistrarManager(); - return instance; -} - -PluginRegistrarManager::PluginRegistrarManager() = default; - -// static -void PluginRegistrarManager::OnRegistrarDestroyed( - FlutterDesktopPluginRegistrarRef registrar) { - GetInstance()->registrars()->erase(registrar); -} - } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc index eee4758c21315..5f1a5b4421f5e 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc @@ -33,36 +33,28 @@ class TestApi : public testing::StubFlutterApi { return message_engine_result; } + // Called for FlutterDesktopMessengerSetCallback. void MessengerSetCallback(const char* channel, FlutterDesktopMessageCallback callback, void* user_data) override { - last_message_callback_set_ = callback; - } - - void RegistrarSetDestructionHandler( - FlutterDesktopOnRegistrarDestroyed callback) override { - last_destruction_callback_set_ = callback; + last_callback_set_ = callback; } const uint8_t* last_data_sent() { return last_data_sent_; } - FlutterDesktopMessageCallback last_message_callback_set() { - return last_message_callback_set_; - } - FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set() { - return last_destruction_callback_set_; + FlutterDesktopMessageCallback last_callback_set() { + return last_callback_set_; } private: const uint8_t* last_data_sent_ = nullptr; - FlutterDesktopMessageCallback last_message_callback_set_ = nullptr; - FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set_ = nullptr; + FlutterDesktopMessageCallback last_callback_set_ = nullptr; }; } // namespace // Tests that the registrar returns a messenger that passes Send through to the // C API. -TEST(PluginRegistrarTest, MessengerSend) { +TEST(MethodCallTest, MessengerSend) { testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); @@ -78,7 +70,7 @@ TEST(PluginRegistrarTest, MessengerSend) { // Tests that the registrar returns a messenger that passes callback // registration and unregistration through to the C API. -TEST(PluginRegistrarTest, MessengerSetMessageHandler) { +TEST(MethodCallTest, MessengerSetMessageHandler) { testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); @@ -93,60 +85,11 @@ TEST(PluginRegistrarTest, MessengerSetMessageHandler) { const size_t message_size, BinaryReply reply) {}; messenger->SetMessageHandler(channel_name, std::move(binary_handler)); - EXPECT_NE(test_api->last_message_callback_set(), nullptr); + EXPECT_NE(test_api->last_callback_set(), nullptr); // Unregister. messenger->SetMessageHandler(channel_name, nullptr); - EXPECT_EQ(test_api->last_message_callback_set(), nullptr); -} - -// Tests that the registrar manager returns the same instance when getting -// the wrapper for the same reference. -TEST(PluginRegistrarTest, ManagerSameInstance) { - testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); - - auto dummy_registrar_handle = - reinterpret_cast(1); - - PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); - EXPECT_EQ(manager->GetRegistrar(dummy_registrar_handle), - manager->GetRegistrar(dummy_registrar_handle)); -} - -// Tests that the registrar manager returns different objects for different -// references. -TEST(PluginRegistrarTest, ManagerDifferentInstances) { - testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); - - auto dummy_registrar_handle_a = - reinterpret_cast(1); - auto dummy_registrar_handle_b = - reinterpret_cast(2); - - PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); - EXPECT_NE(manager->GetRegistrar(dummy_registrar_handle_a), - manager->GetRegistrar(dummy_registrar_handle_b)); -} - -// Tests that the registrar manager deletes wrappers when the underlying -// reference is destroyed. -TEST(PluginRegistrarTest, ManagerRemovesOnDestruction) { - testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); - auto test_api = static_cast(scoped_api_stub.stub()); - - auto dummy_registrar_handle = - reinterpret_cast(1); - PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); - auto* first_wrapper = - manager->GetRegistrar(dummy_registrar_handle); - - // Simulate destruction of the reference. - EXPECT_NE(test_api->last_destruction_callback_set(), nullptr); - test_api->last_destruction_callback_set()(dummy_registrar_handle); - - // Requesting the wrapper should now create a new object. - EXPECT_NE(manager->GetRegistrar(dummy_registrar_handle), - first_wrapper); + EXPECT_EQ(test_api->last_callback_set(), nullptr); } } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc index 580df48645aa9..4bd6ddce01a39 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc @@ -44,14 +44,6 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return reinterpret_cast(1); } -void FlutterDesktopRegistrarSetDestructionHandler( - FlutterDesktopPluginRegistrarRef registrar, - FlutterDesktopOnRegistrarDestroyed callback) { - if (s_stub_implementation) { - s_stub_implementation->RegistrarSetDestructionHandler(callback); - } -} - void FlutterDesktopRegistrarEnableInputBlocking( FlutterDesktopPluginRegistrarRef registrar, const char* channel) { diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h index 284d15e974947..c5f9dbbb2a2a5 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h @@ -34,10 +34,6 @@ class StubFlutterApi { virtual ~StubFlutterApi() {} - // Called for FlutterDesktopRegistrarSetDestructionHandler. - virtual void RegistrarSetDestructionHandler( - FlutterDesktopOnRegistrarDestroyed callback) {} - // Called for FlutterDesktopRegistrarEnableInputBlocking. virtual void RegistrarEnableInputBlocking(const char* channel) {} diff --git a/shell/platform/common/cpp/public/flutter_plugin_registrar.h b/shell/platform/common/cpp/public/flutter_plugin_registrar.h index 95f0abf139608..1caa3cee1f9e4 100644 --- a/shell/platform/common/cpp/public/flutter_plugin_registrar.h +++ b/shell/platform/common/cpp/public/flutter_plugin_registrar.h @@ -18,19 +18,10 @@ extern "C" { // Opaque reference to a plugin registrar. typedef struct FlutterDesktopPluginRegistrar* FlutterDesktopPluginRegistrarRef; -// Function pointer type for registrar destruction callback. -typedef void (*FlutterDesktopOnRegistrarDestroyed)( - FlutterDesktopPluginRegistrarRef); - // Returns the engine messenger associated with this registrar. FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(FlutterDesktopPluginRegistrarRef registrar); -// Registers a callback to be called when the plugin registrar is destroyed. -FLUTTER_EXPORT void FlutterDesktopRegistrarSetDestructionHandler( - FlutterDesktopPluginRegistrarRef registrar, - FlutterDesktopOnRegistrarDestroyed callback); - // Enables input blocking on the given channel. // // If set, then the Flutter window will disable input callbacks diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index 74a5be634694e..fa359076c71f6 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -118,9 +118,6 @@ struct FlutterDesktopPluginRegistrar { // The handle for the window associated with this registrar. FlutterDesktopWindow* window; - - // Callback to be called on registrar destruction. - FlutterDesktopOnRegistrarDestroyed destruction_handler; }; // State associated with the messenger used to communicate with the engine. @@ -685,11 +682,6 @@ FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow( } void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) { - FlutterDesktopPluginRegistrarRef registrar = - controller->plugin_registrar.get(); - if (registrar->destruction_handler) { - registrar->destruction_handler(registrar); - } FlutterEngineShutdown(controller->engine); delete controller; } @@ -840,12 +832,6 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return registrar->messenger.get(); } -void FlutterDesktopRegistrarSetDestructionHandler( - FlutterDesktopPluginRegistrarRef registrar, - FlutterDesktopOnRegistrarDestroyed callback) { - registrar->destruction_handler = callback; -} - FlutterDesktopWindowRef FlutterDesktopRegistrarGetWindow( FlutterDesktopPluginRegistrarRef registrar) { return registrar->window; diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index c53b055fffeb9..25da4374b74bc 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -248,12 +248,6 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return registrar->messenger.get(); } -void FlutterDesktopRegistrarSetDestructionHandler( - FlutterDesktopPluginRegistrarRef registrar, - FlutterDesktopOnRegistrarDestroyed callback) { - registrar->destruction_handler = callback; -} - FlutterDesktopViewRef FlutterDesktopRegistrarGetView( FlutterDesktopPluginRegistrarRef registrar) { return registrar->window; diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index cf69618835373..c0c786799285d 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -15,9 +15,6 @@ Win32FlutterWindow::Win32FlutterWindow(int width, int height) { Win32FlutterWindow::~Win32FlutterWindow() { DestroyRenderSurface(); - if (plugin_registrar_ && plugin_registrar_->destruction_handler) { - plugin_registrar_->destruction_handler(plugin_registrar_.get()); - } } FlutterDesktopViewControllerRef Win32FlutterWindow::CreateWin32FlutterWindow( diff --git a/shell/platform/windows/window_state.h b/shell/platform/windows/window_state.h index 6f0451d7ec940..086f6aebd450f 100644 --- a/shell/platform/windows/window_state.h +++ b/shell/platform/windows/window_state.h @@ -55,9 +55,6 @@ struct FlutterDesktopPluginRegistrar { // The handle for the window associated with this registrar. FlutterDesktopView* window; - - // Callback to be called on registrar destruction. - FlutterDesktopOnRegistrarDestroyed destruction_handler; }; // State associated with the messenger used to communicate with the engine. From b2cc00f078a34d3818771cab63ecdca7f9fe0723 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 7 Apr 2020 10:32:38 -0700 Subject: [PATCH 295/386] Revert "Added errors to match g3 builds and simple errors (#17536)" (#17564) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test failure: [ RUN ] FlutterStringCodec.CanEncodeAndDecodeNil 2020-04-07 10:21:25.091 flutter_channels_unittests[26965:163315] *** Assertion failure in -[FlutterStringCodec encode:], ../../flutter/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm:36 2020-04-07 10:21:25.118 flutter_channels_unittests[26965:163315] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '' *** First throw call stack: ( 0 CoreFoundation 0x00007fff2ccabcf9 __exceptionPreprocess + 256 1 libobjc.A.dylib 0x00007fff5785ea17 objc_exception_throw + 48 2 CoreFoundation 0x00007fff2ccc6a16 +[NSException raise:format:arguments:] + 98 3 Foundation 0x00007fff2ef58e11 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 194 4 flutter_channels_unittests 0x0000000105f1cce2 -[FlutterStringCodec encode:] + 290 5 flutter_channels_unittests 0x0000000105f01bbb _ZN45FlutterStringCodec_CanEncodeAndDecodeNil_Test8TestBodyEv + 107 6 flutter_channels_unittests 0x00000001081d1732 _ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS_4TestEvEET0_PT_MS4_FS3_vEPKc + 146 7 flutter_channels_unittests 0x00000001081a76b0 _ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS_4TestEvEET0_PT_MS4_FS3_vEPKc + 128 8 flutter_channels_unittests 0x00000001081a75c1 _ZN7testing4Test3RunEv + 209 9 flutter_channels_unittests 0x00000001081a8415 _ZN7testing8TestInfo3RunEv + 229 10 flutter_channels_unittests 0x00000001081a95aa _ZN7testing9TestSuite3RunEv + 266 11 flutter_channels_unittests 0x00000001081b4e43 _ZN7testing8internal12UnitTestImpl11RunAllTestsEv + 995 12 flutter_channels_unittests 0x00000001081d8572 _ZN7testing8internal38HandleSehExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + 146 13 flutter_channels_unittests 0x00000001081b49b3 _ZN7testing8internal35HandleExceptionsInMethodIfSupportedINS0_12UnitTestImplEbEET0_PT_MS4_FS3_vEPKc + 131 14 flutter_channels_unittests 0x00000001081b4835 _ZN7testing8UnitTest3RunEv + 197 15 flutter_channels_unittests 0x0000000105f22e73 _Z13RUN_ALL_TESTSv + 35 16 flutter_channels_unittests 0x0000000105f22d79 main + 553 17 libdyld.dylib 0x00007fff5908c3d5 start + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException [ERROR:flutter/fml/backtrace.cc(110)] Caught signal SIGABRT during program execution. Frame 0: 0x7fff591316a6 abort Frame 1: 0x7fff560ea641 __cxa_bad_cast Frame 2: 0x7fff560ea7df default_unexpected_handler()� Frame 3: 0x7fff57860ee3 _objc_terminate()� Frame 4: 0x7fff560f619e std::__terminate(void (*)())� Frame 5: 0x7fff560f5f86 __cxa_get_exception_ptr Frame 6: 0x7fff560e8f99 __cxa_get_globals Frame 7: 0x7fff5785eb51 objc_exception_throw Frame 8: 0x7fff2ccc6a16 +[NSException raise:format:arguments:] Frame 9: 0x7fff2ef58e11 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] Frame 10: 0x105f1cce2 -[FlutterStringCodec encode:] Frame 11: 0x105f01bbb FlutterStringCodec_CanEncodeAndDecodeNil_Test::TestBody()� Frame 12: 0x1081d1732 void testing::internal::HandleSehExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*)� Frame 13: 0x1081a76b0 void testing::internal::HandleExceptionsInMethodIfSupported(testing::Test*, void (testing::Test::*)(), char const*)� Frame 14: 0x1081a75c1 testing::Test::Run()� Frame 15: 0x1081a8415 testing::TestInfo::Run()� Frame 16: 0x1081a95aa testing::TestSuite::Run()� Frame 17: 0x1081b4e43 testing::internal::UnitTestImpl::RunAllTests()� Frame 18: 0x1081d8572 bool testing::internal::HandleSehExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*)� Frame 19: 0x1081b49b3 bool testing::internal::HandleExceptionsInMethodIfSupported(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*)� Frame 20: 0x1081b4835 testing::UnitTest::Run()� Frame 21: 0x105f22e73 RUN_ALL_TESTS()� Frame 22: 0x105f22d79 main Frame 23: 0x7fff5908c3d5 start This reverts commit d1c90b4284282f9745ecd7c65aa72c612df671c1. --- common/config.gni | 8 -------- fml/BUILD.gn | 3 --- shell/platform/darwin/BUILD.gn | 3 --- shell/platform/darwin/common/BUILD.gn | 7 ------- .../darwin/common/framework/Source/FlutterCodecs.mm | 9 +++------ shell/platform/darwin/ios/BUILD.gn | 3 --- .../darwin/ios/framework/Source/FlutterAppDelegate.mm | 2 ++ 7 files changed, 5 insertions(+), 30 deletions(-) diff --git a/common/config.gni b/common/config.gni index a9274612223cd..a3eb20b19a5ae 100644 --- a/common/config.gni +++ b/common/config.gni @@ -68,11 +68,3 @@ if (is_fuchsia) { if ((is_ios || is_mac) && defined(enable_bitcode)) { flutter_enable_bitcode = enable_bitcode } - -if (is_ios || is_mac) { - flutter_cflags_objc = [ - "-Werror=overriding-method-mismatch", - "-Werror=undeclared-selector", - ] - flutter_cflags_objcc = flutter_cflags_objc -} diff --git a/fml/BUILD.gn b/fml/BUILD.gn index f72ac64bc6a6f..9b5626e787b1c 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -120,9 +120,6 @@ source_set("fml") { } if (is_ios || is_mac) { - cflags_objc = flutter_cflags_objc - cflags_objcc = flutter_cflags_objcc - sources += [ "platform/darwin/cf_utils.cc", "platform/darwin/cf_utils.h", diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index bfc29dfe79d76..f5271ca366e60 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -20,9 +20,6 @@ group("darwin") { } source_set("flutter_channels") { - cflags_objc = flutter_cflags_objc - cflags_objcc = flutter_cflags_objcc - sources = [ "common/buffer_conversions.h", "common/buffer_conversions.mm", diff --git a/shell/platform/darwin/common/BUILD.gn b/shell/platform/darwin/common/BUILD.gn index 45a9e3092b2bc..8c5663e84c415 100644 --- a/shell/platform/darwin/common/BUILD.gn +++ b/shell/platform/darwin/common/BUILD.gn @@ -2,13 +2,9 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import("//flutter/common/config.gni") import("framework_shared.gni") source_set("common") { - cflags_objc = flutter_cflags_objc - cflags_objcc = flutter_cflags_objcc - sources = [ "buffer_conversions.h", "buffer_conversions.mm", @@ -38,9 +34,6 @@ config("framework_relative_headers") { # Framework code shared between iOS and macOS. source_set("framework_shared") { - cflags_objc = flutter_cflags_objc - cflags_objcc = flutter_cflags_objcc - sources = [ "framework/Source/FlutterChannels.mm", "framework/Source/FlutterCodecs.mm", diff --git a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm index 6e9b60cbd7359..b06dab7b85577 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm @@ -13,8 +13,7 @@ + (instancetype)sharedInstance { return _sharedInstance; } -- (NSData*)encode:(id)message { - NSAssert([message isKindOfClass:[NSData class]], @""); +- (NSData*)encode:(NSData*)message { return message; } @@ -32,12 +31,10 @@ + (instancetype)sharedInstance { return _sharedInstance; } -- (NSData*)encode:(id)message { - NSAssert([message isKindOfClass:[NSString class]], @""); - NSString* stringMessage = message; +- (NSData*)encode:(NSString*)message { if (message == nil) return nil; - const char* utf8 = stringMessage.UTF8String; + const char* utf8 = message.UTF8String; return [NSData dataWithBytes:utf8 length:strlen(utf8)]; } diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index d9be6e2a9f11e..c98e2b31bf321 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -46,9 +46,6 @@ shared_library("create_flutter_framework_dylib") { public = _flutter_framework_headers - cflags_objc = flutter_cflags_objc - cflags_objcc = flutter_cflags_objcc - sources = [ "framework/Source/FlutterAppDelegate.mm", "framework/Source/FlutterBinaryMessengerRelay.mm", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 3a6d113b5e48b..204261cf2b109 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -8,6 +8,8 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h" +#pragma GCC diagnostic error "-Woverriding-method-mismatch" + static NSString* kUIBackgroundMode = @"UIBackgroundModes"; static NSString* kRemoteNotificationCapabitiliy = @"remote-notification"; static NSString* kBackgroundFetchCapatibility = @"fetch"; From 47c607a0f1916805d664ac3182e7a3dbab158d6b Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 7 Apr 2020 14:23:23 -0400 Subject: [PATCH 296/386] Roll src/third_party/dart 275a76f2fcd8..dcdc71d7639a (31 commits) (#17562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://dart.googlesource.com/sdk.git/+log/275a76f2fcd8..dcdc71d7639a git log 275a76f2fcd8..dcdc71d7639a --date=short --first-parent --format='%ad %ae %s' 2020-04-07 scheglov@google.com Report CONFLICTING_GENERIC_INTERFACES using ClassHierarchy. 2020-04-07 eernst@google.com Adjust member signature expectations for mixed inheritance 2020-04-07 kustermann@google.com [vm/compiler] Move runtime call of Throw/ReThrow/AssertBoolean/InstanceOf out into a Stub 2020-04-07 cskau@google.com [Compiler] Adds a set of generic AllocateObject stubs. 2020-04-07 scheglov@google.com Update constants_2018/constant_types_test to allow type parameters in 'as'. 2020-04-07 alexmarkov@google.com [vm/tests] Speed up / filter slow vm/dart/minimal_kernel*_test 2020-04-07 leafp@google.com Add tests for extended GLB. 2020-04-07 sra@google.com [dart2js] createFromStaticType for LegacyType 2020-04-07 rnystrom@google.com Migrate language_2/covariant to NNBD. 2020-04-07 srujzs@google.com [dart:html] Fix named args in js_typed_interop tests 2020-04-06 rnystrom@google.com Migrate language_2/constants_2018 to NNBD. 2020-04-06 scheglov@google.com Update language/constructor/return_test 2020-04-06 scheglov@google.com Update ClassHierarchy for legacy support, record errors. 2020-04-06 rmacnak@google.com Revert "[vm, gc] Parallel scavenge." 2020-04-06 srujzs@google.com [dart:html] Add nullability to item type for lists 2020-04-06 srujzs@google.com [dart:html] Remove 2.7 annotation from lib/html 2020-04-06 srujzs@google.com [dart:html] Migrate tests with only null asserts 2020-04-06 srujzs@google.com [dart:html] Migrate tests that need changes to NNBD 2020-04-06 srujzs@google.com [dart:html] Migrate more js tests to NNBD 2020-04-06 srujzs@google.com [dart:html] Migrate html tests with dynamic list conversions 2020-04-06 srujzs@google.com [dart:html] Migrate htmlcollection_test to null-safe 2020-04-06 rmacnak@google.com [vm, gc] Parallel scavenge. 2020-04-06 jwren@google.com Update the completion_metrics.dart algorithm to have 2 overlay options 2020-04-06 scheglov@google.com Clear class hierarchies to reduce memory consumption. 2020-04-06 rileyporter@google.com [tests] Removing more List constructor usage in corelib tests. 2020-04-06 liama@google.com [vm] Fix bug in late field reloading 2020-04-06 mfairhurst@google.com [nnbd_migration] Add sanity checks before writing to disk. 2020-04-06 mfairhurst@google.com [nnbd_migration] Change edit links to buttons. 2020-04-06 mehmetf@google.com Reland "[dart:_http] Allow the embedder to prohibit HTTP traffic." 2020-04-06 rmacnak@google.com [vm] Include the handler function in HandleMessage timeline events. 2020-04-06 scheglov@google.com Allow potentially constant type expression in 'as'. Created with: gclient setdep -r src/third_party/dart@dcdc71d7639a If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6539c47f634a1..6f750882b44a5 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '275a76f2fcd879042cdca1d8d9a6f2c1dad4b4f9', + 'dart_revision': 'dcdc71d7639ae83e011cc8d43121397021e35d08', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 35b09e305869a..960a1c3b3af69 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 1f4f76e288d7a69844dd6193c60b2cb3 +Signature: 6ff2f5c3f9547985386c5d29d4246044 UNUSED LICENSES: @@ -7796,9 +7796,11 @@ FILE: ../../../third_party/dart/runtime/vm/visitor.cc FILE: ../../../third_party/dart/samples/ffi/async/async_test.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_async_callback.dart FILE: ../../../third_party/dart/samples/ffi/async/sample_native_port_call.dart +FILE: ../../../third_party/dart/sdk/lib/_http/embedder_config.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/js_runtime/lib/js_patch.dart FILE: ../../../third_party/dart/sdk/lib/_internal/vm/lib/ffi_struct_patch.dart +FILE: ../../../third_party/dart/sdk_nnbd/lib/_http/embedder_config.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_dev_runtime/patch/js_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/js_runtime/lib/js_patch.dart FILE: ../../../third_party/dart/sdk_nnbd/lib/_internal/vm/lib/ffi_struct_patch.dart From e2fcc66fc68e438bb953e97af0e92342ff59d2df Mon Sep 17 00:00:00 2001 From: chunhtai <47866232+chunhtai@users.noreply.github.com> Date: Tue, 7 Apr 2020 11:24:11 -0700 Subject: [PATCH 297/386] always forward move event (#17541) flutter/flutter#54022 The original issue is the _MotionEventsDispatcher depends on all down pointers to send out their own move events. If the third pointer has the movement of zero, it will get filter out by the PointerDataPacketConverter. This result with only two pointer move events been sent to RenderAndroidView, and they both get ignored because it is waiting for the third one. This pr fixes it by always forward the move event --- .../window/pointer_data_packet_converter.cc | 8 ++-- ...pointer_data_packet_converter_unittests.cc | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/ui/window/pointer_data_packet_converter.cc b/lib/ui/window/pointer_data_packet_converter.cc index c7f643f25417d..b945e7b1463c4 100644 --- a/lib/ui/window/pointer_data_packet_converter.cc +++ b/lib/ui/window/pointer_data_packet_converter.cc @@ -169,11 +169,9 @@ void PointerDataPacketConverter::ConvertPointerData( PointerState state = iter->second; FML_DCHECK(state.isDown); - if (LocationNeedsUpdate(pointer_data, state)) { - UpdatePointerIdentifier(pointer_data, state, false); - UpdateDeltaAndState(pointer_data, state); - converted_pointers.push_back(pointer_data); - } + UpdatePointerIdentifier(pointer_data, state, false); + UpdateDeltaAndState(pointer_data, state); + converted_pointers.push_back(pointer_data); break; } case PointerData::Change::kUp: { diff --git a/lib/ui/window/pointer_data_packet_converter_unittests.cc b/lib/ui/window/pointer_data_packet_converter_unittests.cc index d18b7f3e474ed..926637244f8b6 100644 --- a/lib/ui/window/pointer_data_packet_converter_unittests.cc +++ b/lib/ui/window/pointer_data_packet_converter_unittests.cc @@ -244,6 +244,43 @@ TEST(PointerDataPacketConverterTest, CanUpdatePointerIdentifier) { ASSERT_EQ(result[6].synthesized, 0); } +TEST(PointerDataPacketConverterTest, AlwaysForwardMoveEvent) { + PointerDataPacketConverter converter; + auto packet = std::make_unique(4); + PointerData data; + CreateSimulatedPointerData(data, PointerData::Change::kAdd, 0, 0.0, 0.0); + packet->SetPointerData(0, data); + CreateSimulatedPointerData(data, PointerData::Change::kDown, 0, 0.0, 0.0); + packet->SetPointerData(1, data); + // Creates a move event without a location change. + CreateSimulatedPointerData(data, PointerData::Change::kMove, 0, 0.0, 0.0); + packet->SetPointerData(2, data); + CreateSimulatedPointerData(data, PointerData::Change::kUp, 0, 0.0, 0.0); + packet->SetPointerData(3, data); + + auto converted_packet = converter.Convert(std::move(packet)); + + std::vector result; + UnpackPointerPacket(result, std::move(converted_packet)); + + ASSERT_EQ(result.size(), (size_t)4); + ASSERT_EQ(result[0].change, PointerData::Change::kAdd); + ASSERT_EQ(result[0].synthesized, 0); + + ASSERT_EQ(result[1].change, PointerData::Change::kDown); + ASSERT_EQ(result[1].pointer_identifier, 1); + ASSERT_EQ(result[1].synthesized, 0); + + // Does not filter out the move event. + ASSERT_EQ(result[2].change, PointerData::Change::kMove); + ASSERT_EQ(result[2].pointer_identifier, 1); + ASSERT_EQ(result[2].synthesized, 0); + + ASSERT_EQ(result[3].change, PointerData::Change::kUp); + ASSERT_EQ(result[3].pointer_identifier, 1); + ASSERT_EQ(result[3].synthesized, 0); +} + TEST(PointerDataPacketConverterTest, CanWorkWithDifferentDevices) { PointerDataPacketConverter converter; auto packet = std::make_unique(12); From 3190d54a69afd62cfead598be2cea6b6d06dbd30 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Tue, 7 Apr 2020 11:24:52 -0700 Subject: [PATCH 298/386] Add the Android SDK lambda stub library to the classpath (#17508) Previously the build scripts obtained the lambda support classes from rt.jar, which is no longer provided by recent versions of the JDK. --- shell/platform/android/BUILD.gn | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 940801e7f3e03..77d4192cb5001 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -255,15 +255,24 @@ action("flutter_shell_java") { source_jar_path, source_jar_path + ".md5.stamp", ] - inputs = [ android_sdk_jar ] + embedding_dependencies_jars + + lambda_jar = "$android_sdk_build_tools/core-lambda-stubs.jar" + inputs = [ + android_sdk_jar, + lambda_jar, + ] + embedding_dependencies_jars _rebased_current_path = rebase_path(".") _rebased_jar_path = rebase_path(jar_path, root_build_dir) _rebased_source_jar_path = rebase_path(source_jar_path, root_build_dir) _rebased_depfile = rebase_path(depfile, root_build_dir) _rebased_android_sdk_jar = rebase_path(android_sdk_jar, root_build_dir) - _rebased_classpath = [ _rebased_android_sdk_jar ] + - rebase_path(embedding_dependencies_jars, root_build_dir) + _rebased_lambda_jar = rebase_path(lambda_jar, root_build_dir) + _rebased_classpath = + [ + _rebased_android_sdk_jar, + _rebased_lambda_jar, + ] + rebase_path(embedding_dependencies_jars, root_build_dir) args = [ "--depfile=$_rebased_depfile", From ac574e11a0e59d56b361da881dcbc44ce3438c12 Mon Sep 17 00:00:00 2001 From: Ryoichi Izumita Date: Wed, 8 Apr 2020 03:27:15 +0900 Subject: [PATCH 299/386] Fixed a bug that left a blank at the bottom of the screen (#17474) I fixed a bug that left a blank at the bottom of the screen when the iPad's split keyboard was hidden. I also had the same problem with this issue, so I fixed it. iPad split keyboard cause a blank space problem There is no function to change the space size at the bottom of the screen when the split keyboard is moved. This is because it's not clear what the keyboard should do when it's moved to the top of the screen. --- .../framework/Source/FlutterViewController.mm | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index e6906d8d07591..2c5f4ebb6d3b0 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -953,13 +953,23 @@ - (void)updateViewportPadding { - (void)keyboardWillChangeFrame:(NSNotification*)notification { NSDictionary* info = [notification userInfo]; - CGFloat bottom = CGRectGetHeight([[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]); - CGFloat scale = [UIScreen mainScreen].scale; + CGRect keyboardFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; + CGRect screenRect = [[UIScreen mainScreen] bounds]; + + // Considering the iPad's split keyboard, Flutter needs to check if the keyboard frame is present + // in the screen to see if the keyboard is visible. + if (CGRectIntersectsRect(keyboardFrame, screenRect)) { + CGFloat bottom = CGRectGetHeight(keyboardFrame); + CGFloat scale = [UIScreen mainScreen].scale; + + // The keyboard is treated as an inset since we want to effectively reduce the window size by + // the keyboard height. The Dart side will compute a value accounting for the keyboard-consuming + // bottom padding. + _viewportMetrics.physical_view_inset_bottom = bottom * scale; + } else { + _viewportMetrics.physical_view_inset_bottom = 0; + } - // The keyboard is treated as an inset since we want to effectively reduce the window size by the - // keyboard height. The Dart side will compute a value accounting for the keyboard-consuming - // bottom padding. - _viewportMetrics.physical_view_inset_bottom = bottom * scale; [self updateViewportMetrics]; } From 394ac6b4845d91996dbe4e52244898aee512cf3d Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Tue, 7 Apr 2020 11:34:33 -0700 Subject: [PATCH 300/386] [web] fix clipboard.getData (#17544) * remove the fake reply for paste message. the clipboard.dart will reply with the content of the clipboard * also remove the fake answer for copy --- lib/web_ui/lib/src/engine/window.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index a3da6d2dbcc00..bfe950428e8b7 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -392,11 +392,9 @@ class EngineWindow extends ui.Window { return; case 'Clipboard.setData': ClipboardMessageHandler().setDataMethodCall(decoded, callback); - _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'Clipboard.getData': ClipboardMessageHandler().getDataMethodCall(callback); - _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; } break; From fd10734e50680c273a1775f9ff8f75254cb294d0 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 7 Apr 2020 13:19:49 -0700 Subject: [PATCH 301/386] Fix flutter_windows_unittests runtime dependency (#17524) The transitive dependency on the embedder library brings in the right runtime for the build mode, so directly depending on the JIT version isn't necessary, and causes duplicate symbol issues in release builds. --- shell/platform/windows/BUILD.gn | 5 ----- 1 file changed, 5 deletions(-) diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 88869ace762eb..5de1a712a482e 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -123,11 +123,6 @@ executable("flutter_windows_unittests") { ":flutter_windows_headers", ":flutter_windows_source", "//flutter/testing", - - # TODO(chunhtai): Consider refactoring flutter_root/testing so that there's a testing - # target that doesn't require a Dart runtime to be linked in. - # https://github.com/flutter/flutter/issues/41414. - "//third_party/dart/runtime:libdart_jit", "//third_party/rapidjson", ] } From 8f6590cbbc9441f5a69c4a2fe21a4793fdea8808 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Tue, 7 Apr 2020 13:28:42 -0700 Subject: [PATCH 302/386] Added "unrecognized-selector" errors to match g3 builds (#17566) Added "unrecognized-selector" errors to match g3 builds, added "overriding-method-mismatch" to catch a common error. --- common/config.gni | 8 ++++++++ fml/BUILD.gn | 3 +++ shell/platform/darwin/BUILD.gn | 3 +++ shell/platform/darwin/common/BUILD.gn | 7 +++++++ .../darwin/common/framework/Source/FlutterCodecs.mm | 9 ++++++--- shell/platform/darwin/ios/BUILD.gn | 3 +++ .../darwin/ios/framework/Source/FlutterAppDelegate.mm | 2 -- 7 files changed, 30 insertions(+), 5 deletions(-) diff --git a/common/config.gni b/common/config.gni index a3eb20b19a5ae..a9274612223cd 100644 --- a/common/config.gni +++ b/common/config.gni @@ -68,3 +68,11 @@ if (is_fuchsia) { if ((is_ios || is_mac) && defined(enable_bitcode)) { flutter_enable_bitcode = enable_bitcode } + +if (is_ios || is_mac) { + flutter_cflags_objc = [ + "-Werror=overriding-method-mismatch", + "-Werror=undeclared-selector", + ] + flutter_cflags_objcc = flutter_cflags_objc +} diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 9b5626e787b1c..f72ac64bc6a6f 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -120,6 +120,9 @@ source_set("fml") { } if (is_ios || is_mac) { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources += [ "platform/darwin/cf_utils.cc", "platform/darwin/cf_utils.h", diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index f5271ca366e60..bfc29dfe79d76 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -20,6 +20,9 @@ group("darwin") { } source_set("flutter_channels") { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "common/buffer_conversions.h", "common/buffer_conversions.mm", diff --git a/shell/platform/darwin/common/BUILD.gn b/shell/platform/darwin/common/BUILD.gn index 8c5663e84c415..45a9e3092b2bc 100644 --- a/shell/platform/darwin/common/BUILD.gn +++ b/shell/platform/darwin/common/BUILD.gn @@ -2,9 +2,13 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//flutter/common/config.gni") import("framework_shared.gni") source_set("common") { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "buffer_conversions.h", "buffer_conversions.mm", @@ -34,6 +38,9 @@ config("framework_relative_headers") { # Framework code shared between iOS and macOS. source_set("framework_shared") { + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "framework/Source/FlutterChannels.mm", "framework/Source/FlutterCodecs.mm", diff --git a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm index b06dab7b85577..c56fca266abb9 100644 --- a/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm +++ b/shell/platform/darwin/common/framework/Source/FlutterCodecs.mm @@ -13,7 +13,8 @@ + (instancetype)sharedInstance { return _sharedInstance; } -- (NSData*)encode:(NSData*)message { +- (NSData*)encode:(id)message { + NSAssert(!message || [message isKindOfClass:[NSData class]], @""); return message; } @@ -31,10 +32,12 @@ + (instancetype)sharedInstance { return _sharedInstance; } -- (NSData*)encode:(NSString*)message { +- (NSData*)encode:(id)message { if (message == nil) return nil; - const char* utf8 = message.UTF8String; + NSAssert([message isKindOfClass:[NSString class]], @""); + NSString* stringMessage = message; + const char* utf8 = stringMessage.UTF8String; return [NSData dataWithBytes:utf8 length:strlen(utf8)]; } diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index c98e2b31bf321..d9be6e2a9f11e 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -46,6 +46,9 @@ shared_library("create_flutter_framework_dylib") { public = _flutter_framework_headers + cflags_objc = flutter_cflags_objc + cflags_objcc = flutter_cflags_objcc + sources = [ "framework/Source/FlutterAppDelegate.mm", "framework/Source/FlutterBinaryMessengerRelay.mm", diff --git a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm index 204261cf2b109..3a6d113b5e48b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm @@ -8,8 +8,6 @@ #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate_internal.h" -#pragma GCC diagnostic error "-Woverriding-method-mismatch" - static NSString* kUIBackgroundMode = @"UIBackgroundModes"; static NSString* kRemoteNotificationCapabitiliy = @"remote-notification"; static NSString* kBackgroundFetchCapatibility = @"fetch"; From 5b4b1f33c6d6d609c364eeacb76fb41ea7d8fa17 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Tue, 7 Apr 2020 14:11:56 -0700 Subject: [PATCH 303/386] Reland "Improve C++ plugin lifetime handling" (#17570) Relands #17489 with a fix for the unit test flake. The previous unit test relied on the new instance not being created at the same memory address, which isn't guaranteed. --- .../common/cpp/client_wrapper/BUILD.gn | 2 + .../include/flutter/plugin_registrar.h | 48 ++++++++ .../cpp/client_wrapper/plugin_registrar.cc | 18 ++- .../plugin_registrar_unittests.cc | 105 ++++++++++++++++-- .../testing/stub_flutter_api.cc | 8 ++ .../client_wrapper/testing/stub_flutter_api.h | 4 + .../cpp/public/flutter_plugin_registrar.h | 9 ++ shell/platform/glfw/flutter_glfw.cc | 14 +++ shell/platform/windows/flutter_windows.cc | 6 + .../platform/windows/win32_flutter_window.cc | 3 + shell/platform/windows/window_state.h | 3 + 11 files changed, 210 insertions(+), 10 deletions(-) diff --git a/shell/platform/common/cpp/client_wrapper/BUILD.gn b/shell/platform/common/cpp/client_wrapper/BUILD.gn index f46cd81461ed0..ed0bdf9e87536 100644 --- a/shell/platform/common/cpp/client_wrapper/BUILD.gn +++ b/shell/platform/common/cpp/client_wrapper/BUILD.gn @@ -65,4 +65,6 @@ executable("client_wrapper_unittests") { # https://github.com/flutter/flutter/issues/41414. "//third_party/dart/runtime:libdart_jit", ] + + defines = [ "FLUTTER_DESKTOP_LIBRARY" ] } diff --git a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h index 5fbe1b5b6297c..647b7cbb48e8b 100644 --- a/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h +++ b/shell/platform/common/cpp/client_wrapper/include/flutter/plugin_registrar.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ #define FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ +#include #include #include #include @@ -69,6 +70,53 @@ class Plugin { virtual ~Plugin() = default; }; +// A singleton to own PluginRegistrars. This is intended for use in plugins, +// where there is no higher-level object to own a PluginRegistrar that can +// own plugin instances and ensure that they live as long as the engine they +// are registered with. +class PluginRegistrarManager { + public: + static PluginRegistrarManager* GetInstance(); + + // Prevent copying. + PluginRegistrarManager(PluginRegistrarManager const&) = delete; + PluginRegistrarManager& operator=(PluginRegistrarManager const&) = delete; + + // Returns a plugin registrar wrapper of type T, which must be a kind of + // PluginRegistrar, creating it if necessary. The returned registrar will + // live as long as the underlying FlutterDesktopPluginRegistrarRef, so + // can be used to own plugin instances. + // + // Calling this multiple times for the same registrar_ref with different + // template types results in undefined behavior. + template + T* GetRegistrar(FlutterDesktopPluginRegistrarRef registrar_ref) { + auto insert_result = + registrars_.emplace(registrar_ref, std::make_unique(registrar_ref)); + auto& registrar_pair = *(insert_result.first); + FlutterDesktopRegistrarSetDestructionHandler(registrar_pair.first, + OnRegistrarDestroyed); + return static_cast(registrar_pair.second.get()); + } + + // Destroys all registrar wrappers created by the manager. + // + // This is intended primarily for use in tests. + void Reset() { registrars_.clear(); } + + private: + PluginRegistrarManager(); + + using WrapperMap = std::map>; + + static void OnRegistrarDestroyed(FlutterDesktopPluginRegistrarRef registrar); + + WrapperMap* registrars() { return ®istrars_; } + + WrapperMap registrars_; +}; + } // namespace flutter #endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_CLIENT_WRAPPER_INCLUDE_FLUTTER_PLUGIN_REGISTRAR_H_ diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc index 7ca7c3de73b1c..92584a93ee903 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar.cc @@ -138,7 +138,7 @@ void BinaryMessengerImpl::SetMessageHandler(const std::string& channel, ForwardToHandler, message_handler); } -// PluginRegistrar: +// ===== PluginRegistrar ===== PluginRegistrar::PluginRegistrar(FlutterDesktopPluginRegistrarRef registrar) : registrar_(registrar) { @@ -157,4 +157,20 @@ void PluginRegistrar::EnableInputBlockingForChannel( FlutterDesktopRegistrarEnableInputBlocking(registrar_, channel.c_str()); } +// ===== PluginRegistrarManager ===== + +// static +PluginRegistrarManager* PluginRegistrarManager::GetInstance() { + static PluginRegistrarManager* instance = new PluginRegistrarManager(); + return instance; +} + +PluginRegistrarManager::PluginRegistrarManager() = default; + +// static +void PluginRegistrarManager::OnRegistrarDestroyed( + FlutterDesktopPluginRegistrarRef registrar) { + GetInstance()->registrars()->erase(registrar); +} + } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc index 5f1a5b4421f5e..58cb1f5fcaa48 100644 --- a/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc +++ b/shell/platform/common/cpp/client_wrapper/plugin_registrar_unittests.cc @@ -33,28 +33,56 @@ class TestApi : public testing::StubFlutterApi { return message_engine_result; } - // Called for FlutterDesktopMessengerSetCallback. void MessengerSetCallback(const char* channel, FlutterDesktopMessageCallback callback, void* user_data) override { - last_callback_set_ = callback; + last_message_callback_set_ = callback; + } + + void RegistrarSetDestructionHandler( + FlutterDesktopOnRegistrarDestroyed callback) override { + last_destruction_callback_set_ = callback; } const uint8_t* last_data_sent() { return last_data_sent_; } - FlutterDesktopMessageCallback last_callback_set() { - return last_callback_set_; + FlutterDesktopMessageCallback last_message_callback_set() { + return last_message_callback_set_; + } + FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set() { + return last_destruction_callback_set_; } private: const uint8_t* last_data_sent_ = nullptr; - FlutterDesktopMessageCallback last_callback_set_ = nullptr; + FlutterDesktopMessageCallback last_message_callback_set_ = nullptr; + FlutterDesktopOnRegistrarDestroyed last_destruction_callback_set_ = nullptr; +}; + +// A PluginRegistrar whose destruction can be watched for by tests. +class TestPluginRegistrar : public PluginRegistrar { + public: + explicit TestPluginRegistrar(FlutterDesktopPluginRegistrarRef core_registrar) + : PluginRegistrar(core_registrar) {} + + virtual ~TestPluginRegistrar() { + if (destruction_callback_) { + destruction_callback_(); + } + } + + void SetDestructionCallback(std::function callback) { + destruction_callback_ = std::move(callback); + } + + private: + std::function destruction_callback_; }; } // namespace // Tests that the registrar returns a messenger that passes Send through to the // C API. -TEST(MethodCallTest, MessengerSend) { +TEST(PluginRegistrarTest, MessengerSend) { testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); @@ -70,7 +98,7 @@ TEST(MethodCallTest, MessengerSend) { // Tests that the registrar returns a messenger that passes callback // registration and unregistration through to the C API. -TEST(MethodCallTest, MessengerSetMessageHandler) { +TEST(PluginRegistrarTest, MessengerSetMessageHandler) { testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); auto test_api = static_cast(scoped_api_stub.stub()); @@ -85,11 +113,70 @@ TEST(MethodCallTest, MessengerSetMessageHandler) { const size_t message_size, BinaryReply reply) {}; messenger->SetMessageHandler(channel_name, std::move(binary_handler)); - EXPECT_NE(test_api->last_callback_set(), nullptr); + EXPECT_NE(test_api->last_message_callback_set(), nullptr); // Unregister. messenger->SetMessageHandler(channel_name, nullptr); - EXPECT_EQ(test_api->last_callback_set(), nullptr); + EXPECT_EQ(test_api->last_message_callback_set(), nullptr); +} + +// Tests that the registrar manager returns the same instance when getting +// the wrapper for the same reference. +TEST(PluginRegistrarTest, ManagerSameInstance) { + PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); + manager->Reset(); + + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + + auto dummy_registrar_handle = + reinterpret_cast(1); + + EXPECT_EQ(manager->GetRegistrar(dummy_registrar_handle), + manager->GetRegistrar(dummy_registrar_handle)); +} + +// Tests that the registrar manager returns different objects for different +// references. +TEST(PluginRegistrarTest, ManagerDifferentInstances) { + PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); + manager->Reset(); + + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + + auto dummy_registrar_handle_a = + reinterpret_cast(1); + auto dummy_registrar_handle_b = + reinterpret_cast(2); + + EXPECT_NE(manager->GetRegistrar(dummy_registrar_handle_a), + manager->GetRegistrar(dummy_registrar_handle_b)); +} + +// Tests that the registrar manager deletes wrappers when the underlying +// reference is destroyed. +TEST(PluginRegistrarTest, ManagerRemovesOnDestruction) { + PluginRegistrarManager* manager = PluginRegistrarManager::GetInstance(); + manager->Reset(); + + testing::ScopedStubFlutterApi scoped_api_stub(std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + + auto dummy_registrar_handle = + reinterpret_cast(1); + auto* wrapper = + manager->GetRegistrar(dummy_registrar_handle); + + // Simulate destruction of the reference, and ensure that the wrapper + // is destroyed. + EXPECT_NE(test_api->last_destruction_callback_set(), nullptr); + bool destroyed = false; + wrapper->SetDestructionCallback([&destroyed]() { destroyed = true; }); + test_api->last_destruction_callback_set()(dummy_registrar_handle); + EXPECT_EQ(destroyed, true); + + // Requesting the wrapper should now create a new object. + EXPECT_NE(manager->GetRegistrar(dummy_registrar_handle), + nullptr); } } // namespace flutter diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc index 4bd6ddce01a39..580df48645aa9 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.cc @@ -44,6 +44,14 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return reinterpret_cast(1); } +void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback) { + if (s_stub_implementation) { + s_stub_implementation->RegistrarSetDestructionHandler(callback); + } +} + void FlutterDesktopRegistrarEnableInputBlocking( FlutterDesktopPluginRegistrarRef registrar, const char* channel) { diff --git a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h index c5f9dbbb2a2a5..284d15e974947 100644 --- a/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h +++ b/shell/platform/common/cpp/client_wrapper/testing/stub_flutter_api.h @@ -34,6 +34,10 @@ class StubFlutterApi { virtual ~StubFlutterApi() {} + // Called for FlutterDesktopRegistrarSetDestructionHandler. + virtual void RegistrarSetDestructionHandler( + FlutterDesktopOnRegistrarDestroyed callback) {} + // Called for FlutterDesktopRegistrarEnableInputBlocking. virtual void RegistrarEnableInputBlocking(const char* channel) {} diff --git a/shell/platform/common/cpp/public/flutter_plugin_registrar.h b/shell/platform/common/cpp/public/flutter_plugin_registrar.h index 1caa3cee1f9e4..95f0abf139608 100644 --- a/shell/platform/common/cpp/public/flutter_plugin_registrar.h +++ b/shell/platform/common/cpp/public/flutter_plugin_registrar.h @@ -18,10 +18,19 @@ extern "C" { // Opaque reference to a plugin registrar. typedef struct FlutterDesktopPluginRegistrar* FlutterDesktopPluginRegistrarRef; +// Function pointer type for registrar destruction callback. +typedef void (*FlutterDesktopOnRegistrarDestroyed)( + FlutterDesktopPluginRegistrarRef); + // Returns the engine messenger associated with this registrar. FLUTTER_EXPORT FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger(FlutterDesktopPluginRegistrarRef registrar); +// Registers a callback to be called when the plugin registrar is destroyed. +FLUTTER_EXPORT void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback); + // Enables input blocking on the given channel. // // If set, then the Flutter window will disable input callbacks diff --git a/shell/platform/glfw/flutter_glfw.cc b/shell/platform/glfw/flutter_glfw.cc index fa359076c71f6..74a5be634694e 100644 --- a/shell/platform/glfw/flutter_glfw.cc +++ b/shell/platform/glfw/flutter_glfw.cc @@ -118,6 +118,9 @@ struct FlutterDesktopPluginRegistrar { // The handle for the window associated with this registrar. FlutterDesktopWindow* window; + + // Callback to be called on registrar destruction. + FlutterDesktopOnRegistrarDestroyed destruction_handler; }; // State associated with the messenger used to communicate with the engine. @@ -682,6 +685,11 @@ FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow( } void FlutterDesktopDestroyWindow(FlutterDesktopWindowControllerRef controller) { + FlutterDesktopPluginRegistrarRef registrar = + controller->plugin_registrar.get(); + if (registrar->destruction_handler) { + registrar->destruction_handler(registrar); + } FlutterEngineShutdown(controller->engine); delete controller; } @@ -832,6 +840,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return registrar->messenger.get(); } +void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback) { + registrar->destruction_handler = callback; +} + FlutterDesktopWindowRef FlutterDesktopRegistrarGetWindow( FlutterDesktopPluginRegistrarRef registrar) { return registrar->window; diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 25da4374b74bc..c53b055fffeb9 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -248,6 +248,12 @@ FlutterDesktopMessengerRef FlutterDesktopRegistrarGetMessenger( return registrar->messenger.get(); } +void FlutterDesktopRegistrarSetDestructionHandler( + FlutterDesktopPluginRegistrarRef registrar, + FlutterDesktopOnRegistrarDestroyed callback) { + registrar->destruction_handler = callback; +} + FlutterDesktopViewRef FlutterDesktopRegistrarGetView( FlutterDesktopPluginRegistrarRef registrar) { return registrar->window; diff --git a/shell/platform/windows/win32_flutter_window.cc b/shell/platform/windows/win32_flutter_window.cc index c0c786799285d..cf69618835373 100644 --- a/shell/platform/windows/win32_flutter_window.cc +++ b/shell/platform/windows/win32_flutter_window.cc @@ -15,6 +15,9 @@ Win32FlutterWindow::Win32FlutterWindow(int width, int height) { Win32FlutterWindow::~Win32FlutterWindow() { DestroyRenderSurface(); + if (plugin_registrar_ && plugin_registrar_->destruction_handler) { + plugin_registrar_->destruction_handler(plugin_registrar_.get()); + } } FlutterDesktopViewControllerRef Win32FlutterWindow::CreateWin32FlutterWindow( diff --git a/shell/platform/windows/window_state.h b/shell/platform/windows/window_state.h index 086f6aebd450f..6f0451d7ec940 100644 --- a/shell/platform/windows/window_state.h +++ b/shell/platform/windows/window_state.h @@ -55,6 +55,9 @@ struct FlutterDesktopPluginRegistrar { // The handle for the window associated with this registrar. FlutterDesktopView* window; + + // Callback to be called on registrar destruction. + FlutterDesktopOnRegistrarDestroyed destruction_handler; }; // State associated with the messenger used to communicate with the engine. From 1a0932860b665ca0d539b6253feeb497b915827c Mon Sep 17 00:00:00 2001 From: George Wright Date: Tue, 7 Apr 2020 14:41:01 -0700 Subject: [PATCH 304/386] Roll fuchsia/sdk/core/linux-amd64 from 3yOjK... to VzWN4... (#17569) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6f750882b44a5..e90c3b2e6bbb7 100644 --- a/DEPS +++ b/DEPS @@ -552,7 +552,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': '3yOjKk40jfIT70Xdy4AQhLFZUqv75tVq0hRwuE7WuKUC' + 'version': 'VzWN4x5phvAfhb4PT5j9mgzeNIGwxlfQxJHkhiKnligC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 783c451af72c5..f2d6b002fd63c 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: da0285b14c607814ab017e4c18d31717 +Signature: 4f1e4147fee10dda274e4946a42065f6 UNUSED LICENSES: From e59543708dee9fa1973bd4ed87b67d36ddcfc848 Mon Sep 17 00:00:00 2001 From: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Tue, 7 Apr 2020 14:45:59 -0700 Subject: [PATCH 305/386] iOS UITextInput autocorrection prompt (#13959) --- ci/licenses_golden/licenses_flutter | 1 + .../ios/framework/Source/FlutterEngine.mm | 7 ++++ .../Source/FlutterTextInputDelegate.h | 4 ++- .../framework/Source/FlutterTextInputPlugin.h | 31 ++++++++++++++++ .../Source/FlutterTextInputPlugin.mm | 36 ++++++------------- .../Source/FlutterTextInputPluginTest.m | 32 +++++++++++++++++ .../IosUnitTests.xcodeproj/project.pbxproj | 4 +++ 7 files changed, 88 insertions(+), 27 deletions(-) create mode 100644 shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 08d65c11b2a61..5c9e0ac454d9c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -868,6 +868,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginA FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUmbrellaImport.m FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index bfb4a2d2ff8cd..c3302194b039a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -598,6 +598,13 @@ - (void)performAction:(FlutterTextInputAction)action withClient:(int)client { arguments:@[ @(client), actionString ]]; } +- (void)showAutocorrectionPromptRectForStart:(NSUInteger)start + end:(NSUInteger)end + withClient:(int)client { + [_textInputChannel.get() invokeMethod:@"TextInputClient.showAutocorrectionPromptRect" + arguments:@[ @(client), @(start), @(end) ]]; +} + #pragma mark - Screenshot Delegate - (flutter::Rasterizer::Screenshot)takeScreenshot:(flutter::Rasterizer::ScreenshotType)type diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h index d3451b5bd4cd4..8446a79946d63 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h @@ -34,7 +34,9 @@ typedef NS_ENUM(NSInteger, FlutterFloatingCursorDragState) { - (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state withClient:(int)client withPosition:(NSDictionary*)point; - +- (void)showAutocorrectionPromptRectForStart:(NSUInteger)start + end:(NSUInteger)end + withClient:(int)client; @end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTDELEGATE_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h index 70bd949579ce2..3677c0f78284a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h @@ -36,6 +36,9 @@ @end /** A range of text in the buffer of a Flutter text editing widget. */ +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +FLUTTER_EXPORT +#endif @interface FlutterTextRange : UITextRange @property(nonatomic, readonly) NSRange range; @@ -44,4 +47,32 @@ @end +#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG +FLUTTER_EXPORT +#endif +@interface FlutterTextInputView : UIView + +// UITextInput +@property(nonatomic, readonly) NSMutableString* text; +@property(nonatomic, readonly) NSMutableString* markedText; +@property(readwrite, copy) UITextRange* selectedTextRange; +@property(nonatomic, strong) UITextRange* markedTextRange; +@property(nonatomic, copy) NSDictionary* markedTextStyle; +@property(nonatomic, assign) id inputDelegate; + +// UITextInputTraits +@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; +@property(nonatomic) UITextAutocorrectionType autocorrectionType; +@property(nonatomic) UITextSpellCheckingType spellCheckingType; +@property(nonatomic) BOOL enablesReturnKeyAutomatically; +@property(nonatomic) UIKeyboardAppearance keyboardAppearance; +@property(nonatomic) UIKeyboardType keyboardType; +@property(nonatomic) UIReturnKeyType returnKeyType; +@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; +@property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0)); +@property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0)); + +@property(nonatomic, assign) id textInputDelegate; + +@end #endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 029932489f4fd..f9ddd6621d6d8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -140,32 +140,6 @@ - (id)copyWithZone:(NSZone*)zone { @end -@interface FlutterTextInputView : UIView - -// UITextInput -@property(nonatomic, readonly) NSMutableString* text; -@property(nonatomic, readonly) NSMutableString* markedText; -@property(readwrite, copy) UITextRange* selectedTextRange; -@property(nonatomic, strong) UITextRange* markedTextRange; -@property(nonatomic, copy) NSDictionary* markedTextStyle; -@property(nonatomic, assign) id inputDelegate; - -// UITextInputTraits -@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; -@property(nonatomic) UITextAutocorrectionType autocorrectionType; -@property(nonatomic) UITextSpellCheckingType spellCheckingType; -@property(nonatomic) BOOL enablesReturnKeyAutomatically; -@property(nonatomic) UIKeyboardAppearance keyboardAppearance; -@property(nonatomic) UIKeyboardType keyboardType; -@property(nonatomic) UIReturnKeyType returnKeyType; -@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; -@property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0)); -@property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0)); - -@property(nonatomic, assign) id textInputDelegate; - -@end - @implementation FlutterTextInputView { int _textInputClient; const char* _selectionAffinity; @@ -554,6 +528,16 @@ - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection // physical keyboard. - (CGRect)firstRectForRange:(UITextRange*)range { + // multi-stage text is handled somewhere else. + if (_markedTextRange != nil) { + return CGRectZero; + } + + NSUInteger start = ((FlutterTextPosition*)range.start).index; + NSUInteger end = ((FlutterTextPosition*)range.end).index; + [_textInputDelegate showAutocorrectionPromptRectForStart:start + end:end + withClient:_textInputClient]; // TODO(cbracken) Implement. return CGRectZero; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m new file mode 100644 index 0000000000000..6610d9fe4d8cd --- /dev/null +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m @@ -0,0 +1,32 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import +#include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" + +FLUTTER_ASSERT_ARC + +@interface FlutterTextInputPluginTest : XCTestCase +@end + +@implementation FlutterTextInputPluginTest + +- (void)testAutocorrectionPromptRectAppears { + // Setup test. + id engine = OCMClassMock([FlutterEngine class]); + + FlutterTextInputView* inputView = [[FlutterTextInputView alloc] initWithFrame:CGRectZero]; + inputView.textInputDelegate = engine; + [inputView firstRectForRange:[FlutterTextRange rangeWithNSRange:NSMakeRange(0, 1)]]; + + // Verify behavior. + OCMVerify([engine showAutocorrectionPromptRectForStart:0 end:1 withClient:0]); + + // Clean up mocks + [engine stopMocking]; +} +@end diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index 82f36199ac95a..533ff80fd389c 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 0D6AB72C22BC339F00EEE540 /* libOCMock.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D6AB72522BC336100EEE540 /* libOCMock.a */; }; 0D6AB73F22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */; }; + 3D8AF6182384C5420033B95F /* FlutterTextInputPluginTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -94,6 +95,7 @@ 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterEngineTest.mm; sourceTree = ""; }; 0D6AB71722BC336100EEE540 /* OCMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OCMock.xcodeproj; path = ../../../../../third_party/ocmock/Source/OCMock.xcodeproj; sourceTree = ""; }; 0D6AB73E22BD8F0200EEE540 /* FlutterEngineConfig.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = FlutterEngineConfig.xcconfig; sourceTree = ""; }; + 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterTextInputPluginTest.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -176,6 +178,7 @@ children = ( 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */, 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */, + 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */, 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */, 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */, ); @@ -387,6 +390,7 @@ buildActionMask = 2147483647; files = ( 0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */, + 3D8AF6182384C5420033B95F /* FlutterTextInputPluginTest.m in Sources */, 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.m in Sources */, 0D1CE5D8233430F400E5D880 /* FlutterChannelsTest.m in Sources */, 0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */, From c2ffe6103e86f23ba5073a8d7a5cf7e43d8bb462 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Tue, 7 Apr 2020 18:01:02 -0400 Subject: [PATCH 306/386] Roll src/third_party/skia 3ef77ddf9ec4..b41a420ed8db (35 commits) (#17568) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 122 +++++++++++++++---------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/DEPS b/DEPS index e90c3b2e6bbb7..1e649e87fdf9e 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '3ef77ddf9ec435d781cf65f40b08df3b86b414c6', + 'skia_revision': 'b41a420ed8dbb11cdc076c5b06537538591a88e0', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 2eaf088cab5e6..a914ea38a49d9 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 5d6c2ce77840d4576c5353388581ed1c +Signature: 271d2c9191ff6270bf73c063f2e7cb65 UNUSED LICENSES: @@ -1034,44 +1034,44 @@ FILE: ../../../third_party/skia/infra/bots/jobs.json FILE: ../../../third_party/skia/infra/bots/lottie_web.isolate FILE: ../../../third_party/skia/infra/bots/pathkit.isolate FILE: ../../../third_party/skia/infra/bots/perf_skia_bundled.isolate +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Android_API26.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Android_ASAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Chromebook_GLES.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm-Release-Flutter_Android.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-arm64-Release-Android_Wuffs.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86-devrel-Android_SKQP.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Chromebook_GLES.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Coverage.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-MSAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-OpenCL.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SafeStack.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SkVM.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SkVM_ASAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SwiftShader_MSAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SwiftShader_TSAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-TSAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Tidy.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-Wuffs.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-ANGLE.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-ASAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-CMake.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-Fast.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-NoDEPS.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-Static.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-SwiftShader.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Release-Vulkan.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-EMCC-asmjs-Debug-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-EMCC-asmjs-Release-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-EMCC-wasm-Debug-CanvasKit.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-EMCC-wasm-Debug-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-EMCC-wasm-Release-CanvasKit_CPU.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-EMCC-wasm-Release-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-loongson3a-Release-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86-Debug-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Debug-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Shared_Docker.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_API26.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Android_ASAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Chromebook_GLES.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-arm64-Release-Android_Wuffs.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86-devrel-Android_SKQP.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Coverage.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-MSAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-OpenCL.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SafeStack.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SkVM.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SkVM_ASAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_MSAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-SwiftShader_TSAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-TSAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Tidy.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Wuffs.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-ASAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-CMake.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Fast.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Static.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-SwiftShader.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Vulkan.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-asmjs-Debug-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-asmjs-Release-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Debug-CanvasKit.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Debug-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Release-CanvasKit_CPU.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-EMCC-wasm-Release-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm-Debug-iOS.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-iOS.json @@ -1087,11 +1087,11 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Shared.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Win-Clang-x86_64-Release-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Housekeeper-PerCommit-CheckGeneratedFiles.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/unknown-docker-image.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/builder_name_schema.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/builder_name_schema/examples/full.expected/test.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian9-Clang-x86_64-Release-NoDEPS.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Debian10-Clang-x86_64-Release-NoDEPS.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/checkout/examples/full.expected/cross_repo_trybot.json @@ -1106,8 +1106,8 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Debug-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Android-Clang-Pixel-GPU-Adreno530-arm64-Release-All-Android_Skpbench_Mskp.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All-MSAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Perf-Win2019-Clang-GCE-CPU-AVX2-x86_64-Debug-All-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-GalaxyS7_G930FD-GPU-MaliT880-arm64-Debug-All-Android.json @@ -1115,14 +1115,14 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Nexus5x-GPU-Adreno418-arm64-Release-All-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Android-Clang-Pixel3a-GPU-Adreno615-arm64-Debug-All-Android_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-ChromeOS-Clang-SamsungChromebookPlus-GPU-MaliT860-arm-Release-All.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86_64-Debug-All-Docker.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Debug-All-Coverage.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Mac10.13-Clang-MacBookPro11.5-CPU-AVX2-x86_64-Debug-All-ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_AbandonGpuContext_SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_ProcDump.json @@ -1145,7 +1145,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/gsutil/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/gsutil/examples/full.expected/gsutil_tests.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/infra/examples/full.expected/infra_tests.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/run/examples/full.expected/test.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/Build-Debian9-Clang-x86_64-Release-SKNX_NO_SIMD.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/Build-Debian10-Clang-x86_64-Release-SKNX_NO_SIMD.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/Housekeeper-Weekly-RecreateSKPs.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/integer_issue.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/vars/examples/full.expected/win_test.json @@ -1159,7 +1159,7 @@ FILE: ../../../third_party/skia/infra/bots/recipes/check_generated_files.expecte FILE: ../../../third_party/skia/infra/bots/recipes/compile.expected/Build-Win-Clang-x86-Debug.json FILE: ../../../third_party/skia/infra/bots/recipes/compute_buildstats.expected/normal_bot.json FILE: ../../../third_party/skia/infra/bots/recipes/compute_buildstats.expected/trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/compute_test.expected/Test-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json +FILE: ../../../third_party/skia/infra/bots/recipes/compute_test.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipes/g3_compile.expected/g3_compile_nontrybot.json FILE: ../../../third_party/skia/infra/bots/recipes/g3_compile.expected/g3_compile_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/g3_compile.expected/g3_compile_trybot_failure.json @@ -1169,14 +1169,14 @@ FILE: ../../../third_party/skia/infra/bots/recipes/infra.expected/infra_tests.js FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Android-Clang-Nexus7-CPU-Tegra3-arm-Debug-All-Android.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Ubuntu18-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Valgrind_SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipes/perf.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_canvaskit.expected/Perf-Debian10-EMCC-GCE-CPU-AVX2-wasm-Release-All-CanvasKit.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_canvaskit.expected/pathkit_trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian9-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian10-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/Perf-Debian10-EMCC-GCE-CPU-AVX2-wasm-Release-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_pathkit.expected/pathkit_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/Perf-Android-Clang-AndroidOne-GPU-Mali400MP2-arm-Release-All-Android_SkottieTracing.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-SkottieTracing.json -FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian9-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All-SkottieTracing.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-SkottieTracing.json +FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/Perf-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Release-All-SkottieTracing.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/skottietracing_parse_trace_error.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottietrace.expected/skottietracing_trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/perf_skottiewasm_lottieweb.expected/lottie_web_canvas_perf.json @@ -1195,24 +1195,24 @@ FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Androi FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/Perf-Win10-Clang-Golo-GPU-QuadroP400-x86_64-Release-All-Vulkan_Skpbench_DDLTotal_9x9.json FILE: ../../../third_party/skia/infra/bots/recipes/skpbench.expected/trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/skqp_test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json -FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-arm-Release-Flutter_Android.json -FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian9-Clang-universal-devrel-Android_SKQP.json +FILE: ../../../third_party/skia/infra/bots/recipes/skqp_test.expected/Test-Debian10-Clang-GCE-CPU-AVX2-universal-devrel-All-Android_SKQP.json +FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian10-Clang-arm-Release-Flutter_Android.json +FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Debian10-Clang-universal-devrel-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Mac-Clang-x86_64-Debug-CommandBuffer.json FILE: ../../../third_party/skia/infra/bots/recipes/sync_and_compile.expected/Build-Win10-Clang-x86_64-Release-NoDEPS.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel-GPU-Adreno530-arm-Debug-All-Android_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Android-Clang-Pixel2XL-GPU-Adreno540-arm64-Debug-All-Android.json -FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian9-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json +FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json FILE: ../../../third_party/skia/infra/bots/recipes/test.expected/Test-Win10-Clang-ShuttleC-GPU-GTX960-x86_64-Debug-All-ANGLE.json -FILE: ../../../third_party/skia/infra/bots/recipes/test_canvaskit.expected/Test-Debian9-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_canvaskit.expected/Test-Debian10-EMCC-GCE-GPU-WEBGL1-wasm-Debug-All-CanvasKit.json FILE: ../../../third_party/skia/infra/bots/recipes/test_canvaskit.expected/canvaskit_trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/test_lottie_web.expected/Test-Debian9-none-GCE-CPU-AVX2-x86_64-Debug-All-LottieWeb.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_lottie_web.expected/Test-Debian10-none-GCE-CPU-AVX2-x86_64-Debug-All-LottieWeb.json FILE: ../../../third_party/skia/infra/bots/recipes/test_lottie_web.expected/lottie_web_trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Debug-All-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json -FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian9-EMCC-GCE-CPU-AVX2-wasm-Debug-All-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian10-EMCC-GCE-CPU-AVX2-asmjs-Debug-All-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian10-EMCC-GCE-CPU-AVX2-asmjs-Release-All-PathKit.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/Test-Debian10-EMCC-GCE-CPU-AVX2-wasm-Debug-All-PathKit.json FILE: ../../../third_party/skia/infra/bots/recipes/test_pathkit.expected/pathkit_trybot.json -FILE: ../../../third_party/skia/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian9-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json +FILE: ../../../third_party/skia/infra/bots/recipes/test_skqp_emulator.expected/Test-Debian10-Clang-GCE-CPU-Emulator-x86-devrel-All-Android_SKQP.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_buildstats_results.expected/normal_bot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_buildstats_results.expected/trybot.json FILE: ../../../third_party/skia/infra/bots/recipes/upload_dm_results.expected/alternate_bucket.json From 1b6b609d9dae520943b3db385dc99c08ece15f6c Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Tue, 7 Apr 2020 16:33:52 -0700 Subject: [PATCH 307/386] Remove SceneDisplayLag trace event temporarily (#17571) Working theory is that adding async events with begin times in the past causes the timeline to skip some events. See: https://github.com/flutter/flutter/issues/54095 --- shell/common/rasterizer.cc | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index f360cefe960f0..0a6a060c26382 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -242,7 +242,6 @@ RasterStatus Rasterizer::DoDraw( } FrameTiming timing; - const fml::TimePoint frame_target_time = layer_tree->target_time(); timing.Set(FrameTiming::kBuildStart, layer_tree->build_start()); timing.Set(FrameTiming::kBuildFinish, layer_tree->build_finish()); timing.Set(FrameTiming::kRasterStart, fml::TimePoint::Now()); @@ -272,32 +271,6 @@ RasterStatus Rasterizer::DoDraw( timing.Set(FrameTiming::kRasterFinish, raster_finish_time); delegate_.OnFrameRasterized(timing); - if (raster_finish_time > frame_target_time) { - fml::TimePoint latest_frame_target_time = - delegate_.GetLatestFrameTargetTime(); - const auto frame_budget_millis = delegate_.GetFrameBudget().count(); - if (latest_frame_target_time < raster_finish_time) { - latest_frame_target_time = - latest_frame_target_time + - fml::TimeDelta::FromMillisecondsF(frame_budget_millis); - } - const auto frame_lag = - (latest_frame_target_time - frame_target_time).ToMillisecondsF(); - const int vsync_transitions_missed = round(frame_lag / frame_budget_millis); - fml::tracing::TraceEventAsyncComplete( - "flutter", // category - "SceneDisplayLag", // name - frame_target_time, // begin_time - raster_finish_time, // end_time - "frame_target_time", // arg_key_1 - frame_target_time, // arg_val_1 - "current_frame_target_time", // arg_key_2 - latest_frame_target_time, // arg_val_2 - "vsync_transitions_missed", // arg_key_3 - vsync_transitions_missed // arg_val_3 - ); - } - // Pipeline pressure is applied from a couple of places: // rasterizer: When there are more items as of the time of Consume. // animator (via shell): Frame gets produces every vsync. From 3c1c3e26d4e96d8f6f7a313b5e609da3c164378d Mon Sep 17 00:00:00 2001 From: liyuqian Date: Tue, 7 Apr 2020 16:44:57 -0700 Subject: [PATCH 308/386] Add comments to build_and_test_linux_unopt_debug (#17567) Clarify that the test depends on the framework repo. It may fail if the framework repo is broken, so the engine committer don't need to be in panic. --- .cirrus.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index b2942ded3b186..c58fcb3b3ac65 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -74,6 +74,9 @@ task: test_host_script: | cd $ENGINE_PATH/src ./flutter/testing/run_tests.sh host_release + + # The following test depends on Flutter framework repo. It may fail if the + # framework repo is currently broken. - name: build_and_test_linux_unopt_debug compile_host_script: | cd $ENGINE_PATH/src @@ -97,6 +100,9 @@ task: mkdir -p $FRAMEWORK_PATH cd $FRAMEWORK_PATH git clone https://github.com/flutter/flutter.git + verify_framework_script: + - echo "Checking that the framework Cirrus test is not currently broken..." + - curl -s https://api.cirrus-ci.com/github/flutter/flutter.json | grep -q passing test_web_script: | cd $FRAMEWORK_PATH/flutter/dev/integration_tests/web ../../../bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web From 50af8a2e73e9332d08b30270674d197c4d314537 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 Apr 2020 01:36:02 -0400 Subject: [PATCH 309/386] Roll src/third_party/dart dcdc71d7639a..a90b84544426 (32 commits) (#17585) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 1e649e87fdf9e..aac80211913fd 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'dcdc71d7639ae83e011cc8d43121397021e35d08', + 'dart_revision': 'a90b845444264595ec7193997eceb15b753deb3c', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 960a1c3b3af69..a0f72398dd1c6 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 6ff2f5c3f9547985386c5d29d4246044 +Signature: 9d8aa05182ca08140f68e0d7fbcd9e64 UNUSED LICENSES: From e64e7bd9ed949785bd8b7c89775d2351c1195e44 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 Apr 2020 07:32:47 -0400 Subject: [PATCH 310/386] Roll src/third_party/skia b41a420ed8db..07b2bafbbc3c (13 commits) (#17579) https://skia.googlesource.com/skia.git/+log/b41a420ed8db..07b2bafbbc3c git log b41a420ed8db..07b2bafbbc3c --date=short --first-parent --format='%ad %ae %s' 2020-04-07 mtklein@google.com build conservative stack frame on x86-64 2020-04-07 mtklein@google.com add and test stack load/store 2020-04-07 brianosman@google.com Remove secret wireframe mode from drawVertices 2020-04-07 mtklein@google.com remove "debian9" ASAN jobs 2020-04-07 brianosman@google.com Remove gamma GM 2020-04-07 csmartdalton@google.com Implement support for indirect draws 2020-04-07 scroggo@google.com Upgrade libwebp used for testing to v1.1.0 2020-04-07 robertphillips@google.com Remove GrStrikeCache.h from all other header files 2020-04-07 mtklein@google.com backup plan for ASAN bots 2020-04-07 reed@google.com approximate sine for skvm 2020-04-07 mtklein@google.com remove SK_USE_SKVM_BLITTER 2020-04-07 jlavrova@google.com Mixed languages text crashes 2020-04-07 reed@google.com remove deprecated concat44 -- use concat Created with: gclient setdep -r src/third_party/skia@07b2bafbbc3c If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC csmartdalton@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: csmartdalton@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/DEPS b/DEPS index aac80211913fd..51bd1d7d76697 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'b41a420ed8dbb11cdc076c5b06537538591a88e0', + 'skia_revision': '07b2bafbbc3c72216cebf7b4446229d45b33db5d', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index a914ea38a49d9..b0166ee4c76ad 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 271d2c9191ff6270bf73c063f2e7cb65 +Signature: 813af18c44abf1fb9c0ac15d324a74f5 UNUSED LICENSES: @@ -1046,8 +1046,6 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SK_CPU_LIMIT_SSE41.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SafeStack.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SkVM.json -FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SkVM_ASAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SwiftShader_MSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-SwiftShader_TSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-Clang-x86_64-Debug-TSAN.json @@ -2258,7 +2256,6 @@ FILE: ../../../third_party/skia/gm/drawatlascolor.cpp FILE: ../../../third_party/skia/gm/drawminibitmaprect.cpp FILE: ../../../third_party/skia/gm/fadefilter.cpp FILE: ../../../third_party/skia/gm/fontscalerdistortable.cpp -FILE: ../../../third_party/skia/gm/gamma.cpp FILE: ../../../third_party/skia/gm/image_pict.cpp FILE: ../../../third_party/skia/gm/image_shader.cpp FILE: ../../../third_party/skia/gm/imagefilters.cpp From 4fb39303d34d4a1be0527867a5384130b1e5d468 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 Apr 2020 07:46:03 -0400 Subject: [PATCH 311/386] Roll src/third_party/dart a90b84544426..28c4c6c156e5 (2 commits) (#17586) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 51bd1d7d76697..6fcb22936c125 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': 'a90b845444264595ec7193997eceb15b753deb3c', + 'dart_revision': '28c4c6c156e5c6eb5d3013e9d54ee8a7037e7fa6', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index a0f72398dd1c6..9d3474ce8f380 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 9d8aa05182ca08140f68e0d7fbcd9e64 +Signature: 0385a9d418306c4eadb3be682a5f2a37 UNUSED LICENSES: From 157069cf25040373c101747807fb76dc25e822f6 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 Apr 2020 09:41:02 -0400 Subject: [PATCH 312/386] Roll src/third_party/skia 07b2bafbbc3c..f6860405e1bf (9 commits) (#17587) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 6fcb22936c125..94fd42a895289 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '07b2bafbbc3c72216cebf7b4446229d45b33db5d', + 'skia_revision': 'f6860405e1bf4603cb393c1818454dc3f893a07a', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index b0166ee4c76ad..b52ce8df9f4cb 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 813af18c44abf1fb9c0ac15d324a74f5 +Signature: 91431c0474883af27c1e7c0e3dee8955 UNUSED LICENSES: From 1b48463bf31a73c1b1b7d0ab36a718649a921af2 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Wed, 8 Apr 2020 08:40:04 -0700 Subject: [PATCH 313/386] Change the directory to the scenario app (#17583) --- testing/scenario_app/assemble_apk.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing/scenario_app/assemble_apk.sh b/testing/scenario_app/assemble_apk.sh index 2671adc80c315..9eb3cbfe7e0bc 100755 --- a/testing/scenario_app/assemble_apk.sh +++ b/testing/scenario_app/assemble_apk.sh @@ -1,6 +1,8 @@ #!/bin/bash -"${BASH_SOURCE%/*}/compile_android_aot.sh" $1 $2 +pushd "${BASH_SOURCE%/*}" + ./compile_android_aot.sh "$1" "$2" +popd pushd "${BASH_SOURCE%/*}/android" ./gradlew assembleDebug --no-daemon From f6ca48943d0f5cd4360fd9593a73a9d9c938db56 Mon Sep 17 00:00:00 2001 From: Kaushik Iska Date: Wed, 8 Apr 2020 09:14:11 -0700 Subject: [PATCH 314/386] [perf] Add a SceneDisplayLag event when we miss vsyncs (#17575) This event goes from now -> current vsync target time to avoid the limitations as seen in https://github.com/flutter/flutter/issues/54095#issuecomment-610636237 This event also tags additional metadata to capture `vsync_transitions_missed` considering the refresh rate of the display. --- shell/common/rasterizer.cc | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index 0a6a060c26382..bce1b2bf036cb 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -242,6 +242,7 @@ RasterStatus Rasterizer::DoDraw( } FrameTiming timing; + const fml::TimePoint frame_target_time = layer_tree->target_time(); timing.Set(FrameTiming::kBuildStart, layer_tree->build_start()); timing.Set(FrameTiming::kBuildFinish, layer_tree->build_finish()); timing.Set(FrameTiming::kRasterStart, fml::TimePoint::Now()); @@ -271,6 +272,32 @@ RasterStatus Rasterizer::DoDraw( timing.Set(FrameTiming::kRasterFinish, raster_finish_time); delegate_.OnFrameRasterized(timing); + if (raster_finish_time > frame_target_time) { + fml::TimePoint latest_frame_target_time = + delegate_.GetLatestFrameTargetTime(); + const auto frame_budget_millis = delegate_.GetFrameBudget().count(); + if (latest_frame_target_time < raster_finish_time) { + latest_frame_target_time = + latest_frame_target_time + + fml::TimeDelta::FromMillisecondsF(frame_budget_millis); + } + const auto frame_lag = + (latest_frame_target_time - frame_target_time).ToMillisecondsF(); + const int vsync_transitions_missed = round(frame_lag / frame_budget_millis); + fml::tracing::TraceEventAsyncComplete( + "flutter", // category + "SceneDisplayLag", // name + raster_finish_time, // begin_time + latest_frame_target_time, // end_time + "frame_target_time", // arg_key_1 + frame_target_time, // arg_val_1 + "current_frame_target_time", // arg_key_2 + latest_frame_target_time, // arg_val_2 + "vsync_transitions_missed", // arg_key_3 + vsync_transitions_missed // arg_val_3 + ); + } + // Pipeline pressure is applied from a couple of places: // rasterizer: When there are more items as of the time of Consume. // animator (via shell): Frame gets produces every vsync. From 38cba0757dc6adc797cb66b48d6240a0d554b4c0 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Wed, 8 Apr 2020 11:55:23 -0700 Subject: [PATCH 315/386] Do not enable antialiasing by default in CanvasKit mode (#17534) * Do not enable antialiasing by default in CanvasKit mode * Add comment --- lib/web_ui/lib/src/engine/compositor/surface.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/web_ui/lib/src/engine/compositor/surface.dart b/lib/web_ui/lib/src/engine/compositor/surface.dart index 20f6bfe5532a8..3f8a2c7880479 100644 --- a/lib/web_ui/lib/src/engine/compositor/surface.dart +++ b/lib/web_ui/lib/src/engine/compositor/surface.dart @@ -114,8 +114,12 @@ class Surface { ..position = 'absolute' ..width = '${logicalSize.width.ceil()}px' ..height = '${logicalSize.height.ceil()}px'; - final int glContext = canvasKit - .callMethod('GetWebGLContext', [htmlCanvas]); + final int glContext = canvasKit.callMethod('GetWebGLContext', [ + htmlCanvas, + // Default to no anti-aliasing. Paint commands can be explicitly + // anti-aliased by setting their `Paint` object's `antialias` property. + js.JsObject.jsify({'antialias': 0}), + ]); final js.JsObject grContext = canvasKit.callMethod('MakeGrContext', [glContext]); final js.JsObject skSurface = From 6c1d0af36046b2f7498c4fc82777e5218a176350 Mon Sep 17 00:00:00 2001 From: liyuqian Date: Wed, 8 Apr 2020 12:16:03 -0700 Subject: [PATCH 316/386] Fix typo in shell_benchmarks (#17578) --- .cirrus.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index c58fcb3b3ac65..b505c43f4c7de 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -60,7 +60,7 @@ task: cd $ENGINE_PATH/src/out/host_release/ ./txt_benchmarks --benchmark_format=json > txt_benchmarks.json ./fml_benchmarks --benchmark_format=json > fml_benchmarks.json - ./fml_benchmarks --benchmark_format=json > shell_benchmarks.json + ./shell_benchmarks --benchmark_format=json > shell_benchmarks.json cd $ENGINE_PATH/src/flutter/testing/benchmark pub get dart bin/parse_and_send.dart ../../../out/host_release/txt_benchmarks.json From e7e46336137540aa916a3589342ca129a6fcee1b Mon Sep 17 00:00:00 2001 From: godofredoc <54371434+godofredoc@users.noreply.github.com> Date: Wed, 8 Apr 2020 12:50:17 -0700 Subject: [PATCH 317/386] Add logs between fuchsia test steps. (#17584) * Add logs between fuchsia test steps. Adding logs and date time in between steps will simplify finding the steps that are failing during the fuchsia tests. https://github.com/flutter/flutter/issues/54253 * Use date command instead of DATE.. * Remove extra log line. * Same spacer lenght for all the log lines. --- testing/fuchsia/run_tests.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/testing/fuchsia/run_tests.sh b/testing/fuchsia/run_tests.sh index 90eb36ab8e486..5b40eb4e846fb 100755 --- a/testing/fuchsia/run_tests.sh +++ b/testing/fuchsia/run_tests.sh @@ -36,14 +36,19 @@ reboot() { trap reboot EXIT +echo "$(date) START:PAVING ------------------------------------------" ./fuchsia_ctl -d $device_name pave -i $1 +echo "$(date) END:PAVING --------------------------------------------" +echo "$(date) START:WAIT_DEVICE_READY -------------------------------" for i in {1..10}; do ./fuchsia_ctl -d $device_name ssh -c "echo up" && break || sleep 15; done +echo "$(date) END:WAIT_DEVICE_READY ---------------------------------" # TODO(gw280): Enable tests using JIT runner +echo "$(date) START:flutter_runner_tests ----------------------------" ./fuchsia_ctl -d $device_name test \ -f flutter_aot_runner-0.far \ -f flutter_runner_tests-0.far \ @@ -58,23 +63,26 @@ done # TODO(https://github.com/flutter/flutter/issues/50032) Enable after the # Fuchsia message loop migration is complete. +echo "$(date) START:fml_tests ---------------------------------------" ./fuchsia_ctl -d $device_name test \ -f fml_tests-0.far \ -t fml_tests \ -a "--gtest_filter=-MessageLoop*:Message*:FileTest*" +echo "$(date) START:flow_tests --------------------------------------" ./fuchsia_ctl -d $device_name test \ -f flow_tests-0.far \ -t flow_tests +echo "$(date) START:runtime_tests -----------------------------------" ./fuchsia_ctl -d $device_name test \ -f runtime_tests-0.far \ -t runtime_tests # TODO(https://github.com/flutter/flutter/issues/53399): Re-enable # OnServiceProtocolGetSkSLsWorks once it passes on Fuchsia. +echo "$(date) START:shell_tests -------------------------------------" ./fuchsia_ctl -d $device_name test \ -f shell_tests-0.far \ -t shell_tests \ -a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks" - From 3ddd1ef4b0c3e8e47c8d14a1b2fe2873a26733d6 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Wed, 8 Apr 2020 12:51:43 -0700 Subject: [PATCH 318/386] Implement repeat filtering logic in Android Embedder (#17509) --- .../editing/InputConnectionAdaptor.java | 106 +++++++++++++++--- .../plugin/editing/TextInputPlugin.java | 4 + .../editing/InputConnectionAdaptorTest.java | 74 ++++++++++++ 3 files changed, 171 insertions(+), 13 deletions(-) diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index 93ec036b75ddf..525e2efe8f847 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -37,10 +37,46 @@ class InputConnectionAdaptor extends BaseInputConnection { private int mBatchCount; private InputMethodManager mImm; private final Layout mLayout; - // Used to determine if Samsung-specific hacks should be applied. private final boolean isSamsung; + private boolean mRepeatCheckNeeded = false; + private TextEditingValue mLastSentTextEditngValue; + // Data class used to get and store the last-sent values via updateEditingState to + // the framework. These are then compared against to prevent redundant messages + // with the same data before any valid operations were made to the contents. + private class TextEditingValue { + public int selectionStart; + public int selectionEnd; + public int composingStart; + public int composingEnd; + public String text; + + public TextEditingValue(Editable editable) { + selectionStart = Selection.getSelectionStart(editable); + selectionEnd = Selection.getSelectionEnd(editable); + composingStart = BaseInputConnection.getComposingSpanStart(editable); + composingEnd = BaseInputConnection.getComposingSpanEnd(editable); + text = editable.toString(); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof TextEditingValue)) { + return false; + } + TextEditingValue value = (TextEditingValue) o; + return selectionStart == value.selectionStart + && selectionEnd == value.selectionEnd + && composingStart == value.composingStart + && composingEnd == value.composingEnd + && text.equals(value.text); + } + } + @SuppressWarnings("deprecation") public InputConnectionAdaptor( View view, @@ -76,15 +112,42 @@ private void updateEditingState() { // If the IME is in the middle of a batch edit, then wait until it completes. if (mBatchCount > 0) return; - int selectionStart = Selection.getSelectionStart(mEditable); - int selectionEnd = Selection.getSelectionEnd(mEditable); - int composingStart = BaseInputConnection.getComposingSpanStart(mEditable); - int composingEnd = BaseInputConnection.getComposingSpanEnd(mEditable); + TextEditingValue currentValue = new TextEditingValue(mEditable); + + // Return if this data has already been sent and no meaningful changes have + // occurred to mark this as dirty. This prevents duplicate remote updates of + // the same data, which can break formatters that change the length of the + // contents. + if (mRepeatCheckNeeded && currentValue.equals(mLastSentTextEditngValue)) { + return; + } - mImm.updateSelection(mFlutterView, selectionStart, selectionEnd, composingStart, composingEnd); + mImm.updateSelection( + mFlutterView, + currentValue.selectionStart, + currentValue.selectionEnd, + currentValue.composingStart, + currentValue.composingEnd); textInputChannel.updateEditingState( - mClient, mEditable.toString(), selectionStart, selectionEnd, composingStart, composingEnd); + mClient, + currentValue.text, + currentValue.selectionStart, + currentValue.selectionEnd, + currentValue.composingStart, + currentValue.composingEnd); + + mRepeatCheckNeeded = true; + mLastSentTextEditngValue = currentValue; + } + + // This should be called whenever a change could have been made to + // the value of mEditable, which will make any call of updateEditingState() + // ineligible for repeat checking as we do not want to skip sending real changes + // to the framework. + public void markDirty() { + // Disable updateEditngState's repeat-update check + mRepeatCheckNeeded = false; } @Override @@ -109,7 +172,7 @@ public boolean endBatchEdit() { @Override public boolean commitText(CharSequence text, int newCursorPosition) { boolean result = super.commitText(text, newCursorPosition); - updateEditingState(); + markDirty(); return result; } @@ -118,14 +181,21 @@ public boolean deleteSurroundingText(int beforeLength, int afterLength) { if (Selection.getSelectionStart(mEditable) == -1) return true; boolean result = super.deleteSurroundingText(beforeLength, afterLength); - updateEditingState(); + markDirty(); + return result; + } + + @Override + public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) { + boolean result = super.deleteSurroundingTextInCodePoints(beforeLength, afterLength); + markDirty(); return result; } @Override public boolean setComposingRegion(int start, int end) { boolean result = super.setComposingRegion(start, end); - updateEditingState(); + markDirty(); return result; } @@ -137,7 +207,7 @@ public boolean setComposingText(CharSequence text, int newCursorPosition) { } else { result = super.setComposingText(text, newCursorPosition); } - updateEditingState(); + markDirty(); return result; } @@ -159,7 +229,7 @@ public boolean finishComposingText() { } } - updateEditingState(); + markDirty(); return result; } @@ -173,6 +243,13 @@ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { return extractedText; } + @Override + public boolean clearMetaKeyStates(int states) { + boolean result = super.clearMetaKeyStates(states); + markDirty(); + return result; + } + // Detect if the keyboard is a Samsung keyboard, where we apply Samsung-specific hacks to // fix critical bugs that make the keyboard otherwise unusable. See finishComposingText() for // more details. @@ -197,7 +274,7 @@ private boolean isSamsung() { @Override public boolean setSelection(int start, int end) { boolean result = super.setSelection(start, end); - updateEditingState(); + markDirty(); return result; } @@ -219,6 +296,7 @@ private static int clampIndexToEditable(int index, Editable editable) { @Override public boolean sendKeyEvent(KeyEvent event) { + markDirty(); if (event.getAction() == KeyEvent.ACTION_DOWN) { if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { int selStart = clampIndexToEditable(Selection.getSelectionStart(mEditable), mEditable); @@ -344,6 +422,7 @@ public boolean sendKeyEvent(KeyEvent event) { @Override public boolean performContextMenuAction(int id) { + markDirty(); if (id == android.R.id.selectAll) { setSelection(0, mEditable.length()); return true; @@ -397,6 +476,7 @@ public boolean performContextMenuAction(int id) { @Override public boolean performEditorAction(int actionCode) { + markDirty(); switch (actionCode) { case EditorInfo.IME_ACTION_NONE: textInputChannel.newline(mClient); diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 9d41dc5b52902..17ccea06728de 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -322,6 +322,10 @@ void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { } // Always apply state to selection which handles updating the selection if needed. applyStateToSelection(state); + InputConnection connection = getLastInputConnection(); + if (connection != null && connection instanceof InputConnectionAdaptor) { + ((InputConnectionAdaptor) connection).markDirty(); + } // Use updateSelection to update imm on selection if it is not neccessary to restart. if (!restartAlwaysRequired && !mRestartInputPending) { mImm.updateSelection( diff --git a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java index 12d042c0237f6..bf0f513a0835e 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java @@ -270,6 +270,49 @@ public void testMethod_getExtractedText() { assertEquals(extractedText.selectionEnd, selStart); } + @Test + public void inputConnectionAdaptor_RepeatFilter() throws NullPointerException { + View testView = new View(RuntimeEnvironment.application); + FlutterJNI mockFlutterJni = mock(FlutterJNI.class); + DartExecutor dartExecutor = spy(new DartExecutor(mockFlutterJni, mock(AssetManager.class))); + int inputTargetId = 0; + TestTextInputChannel textInputChannel = new TestTextInputChannel(dartExecutor); + Editable mEditable = Editable.Factory.getInstance().newEditable(""); + Editable spyEditable = spy(mEditable); + EditorInfo outAttrs = new EditorInfo(); + outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE; + + InputConnectionAdaptor inputConnectionAdaptor = + new InputConnectionAdaptor( + testView, inputTargetId, textInputChannel, spyEditable, outAttrs); + + inputConnectionAdaptor.beginBatchEdit(); + assertEquals(textInputChannel.updateEditingStateInvocations, 0); + inputConnectionAdaptor.setComposingText("I do not fear computers. I fear the lack of them.", 1); + assertEquals(textInputChannel.text, null); + assertEquals(textInputChannel.updateEditingStateInvocations, 0); + inputConnectionAdaptor.endBatchEdit(); + assertEquals(textInputChannel.updateEditingStateInvocations, 1); + assertEquals(textInputChannel.text, "I do not fear computers. I fear the lack of them."); + + inputConnectionAdaptor.beginBatchEdit(); + assertEquals(textInputChannel.updateEditingStateInvocations, 1); + inputConnectionAdaptor.endBatchEdit(); + assertEquals(textInputChannel.updateEditingStateInvocations, 1); + + inputConnectionAdaptor.beginBatchEdit(); + assertEquals(textInputChannel.text, "I do not fear computers. I fear the lack of them."); + assertEquals(textInputChannel.updateEditingStateInvocations, 1); + inputConnectionAdaptor.setSelection(3, 4); + assertEquals(textInputChannel.updateEditingStateInvocations, 1); + assertEquals(textInputChannel.selectionStart, 49); + assertEquals(textInputChannel.selectionEnd, 49); + inputConnectionAdaptor.endBatchEdit(); + assertEquals(textInputChannel.updateEditingStateInvocations, 2); + assertEquals(textInputChannel.selectionStart, 3); + assertEquals(textInputChannel.selectionEnd, 4); + } + private static final String SAMPLE_TEXT = "Lorem ipsum dolor sit amet," + "\nconsectetur adipiscing elit."; @@ -285,4 +328,35 @@ private static InputConnectionAdaptor sampleInputConnectionAdaptor(Editable edit TextInputChannel textInputChannel = mock(TextInputChannel.class); return new InputConnectionAdaptor(testView, client, textInputChannel, editable, null); } + + private class TestTextInputChannel extends TextInputChannel { + public TestTextInputChannel(DartExecutor dartExecutor) { + super(dartExecutor); + } + + public int inputClientId; + public String text; + public int selectionStart; + public int selectionEnd; + public int composingStart; + public int composingEnd; + public int updateEditingStateInvocations = 0; + + @Override + public void updateEditingState( + int inputClientId, + String text, + int selectionStart, + int selectionEnd, + int composingStart, + int composingEnd) { + this.inputClientId = inputClientId; + this.text = text; + this.selectionStart = selectionStart; + this.selectionEnd = selectionEnd; + this.composingStart = composingStart; + this.composingEnd = composingEnd; + updateEditingStateInvocations++; + } + } } From b0b67ef9e235f9ec5a28f86c0e04178c96252ca2 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Wed, 8 Apr 2020 13:51:38 -0700 Subject: [PATCH 319/386] [web] Fix window.defaultRouteName (#17580) --- .../lib/src/engine/browser_location.dart | 26 ++----------------- lib/web_ui/lib/src/engine/test_embedding.dart | 2 +- lib/web_ui/lib/src/engine/window.dart | 4 +++ lib/web_ui/test/engine/history_test.dart | 2 +- lib/web_ui/test/window_test.dart | 26 +++++++++++++++++++ 5 files changed, 34 insertions(+), 26 deletions(-) diff --git a/lib/web_ui/lib/src/engine/browser_location.dart b/lib/web_ui/lib/src/engine/browser_location.dart index e2f0cfc4d58ab..30bcfcdf64c37 100644 --- a/lib/web_ui/lib/src/engine/browser_location.dart +++ b/lib/web_ui/lib/src/engine/browser_location.dart @@ -9,27 +9,6 @@ part of engine; // Some parts of this file were inspired/copied from the AngularDart router. -/// Ensures that `str` is prefixed with `leading`. -/// -/// If `str` is already prefixed, it'll be returned unchanged. If it's not, -/// this function will prefix it. -/// -/// The `applyWhenEmpty` flag controls whether this function should prefix `str` -/// or not when it's an empty string. -/// -/// ```dart -/// ensureLeading('/path', '/'); // "/path" -/// ensureLeading('path', '/'); // "/path" -/// ensureLeading('', '/'); // "/" -/// ensureLeading('', '/', applyWhenEmpty: false); // "" -/// ``` -String ensureLeading(String str, String leading, {bool applyWhenEmpty = true}) { - if (str.isEmpty && !applyWhenEmpty) { - return str; - } - return str.startsWith(leading) ? str : '$leading$str'; -} - /// [LocationStrategy] is responsible for representing and reading route state /// from the browser's URL. /// @@ -93,12 +72,11 @@ class HashLocationStrategy extends LocationStrategy { // the hash value is always prefixed with a `#` // and if it is empty then it will stay empty String path = _platformLocation.hash ?? ''; + assert(path.isEmpty || path.startsWith('#')); // Dart will complain if a call to substring is // executed with a position value that exceeds the // length of string. - path = path.isEmpty ? path : path.substring(1); - // The path, by convention, should always contain a leading '/'. - return ensureLeading(path, '/'); + return path.isEmpty ? path : path.substring(1); } @override diff --git a/lib/web_ui/lib/src/engine/test_embedding.dart b/lib/web_ui/lib/src/engine/test_embedding.dart index 713fddf712ca6..c1295a2ff0921 100644 --- a/lib/web_ui/lib/src/engine/test_embedding.dart +++ b/lib/web_ui/lib/src/engine/test_embedding.dart @@ -37,7 +37,7 @@ class TestLocationStrategy extends LocationStrategy { history = [initialEntry]; @override - String get path => ensureLeading(currentEntry.url, '/'); + String get path => currentEntry.url; int _currentEntryIndex; int get currentEntryIndex => _currentEntryIndex; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index bfe950428e8b7..298f79ec0c831 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -441,6 +441,10 @@ class EngineWindow extends ui.Window { _replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); break; } + // As soon as Flutter starts taking control of the app navigation, we + // should reset [_defaultRouteName] to "/" so it doesn't have any + // further effect after this point. + _defaultRouteName = '/'; return; } diff --git a/lib/web_ui/test/engine/history_test.dart b/lib/web_ui/test/engine/history_test.dart index c7c31b2cbd124..494b565ddd4c4 100644 --- a/lib/web_ui/test/engine/history_test.dart +++ b/lib/web_ui/test/engine/history_test.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. // @dart = 2.6 -@TestOn('vm && linux') +@TestOn('!safari') // TODO(nurhan): https://github.com/flutter/flutter/issues/51169 import 'dart:async'; diff --git a/lib/web_ui/test/window_test.dart b/lib/web_ui/test/window_test.dart index a20bffe3877df..4062be970c3f1 100644 --- a/lib/web_ui/test/window_test.dart +++ b/lib/web_ui/test/window_test.dart @@ -3,9 +3,15 @@ // found in the LICENSE file. // @dart = 2.6 +import 'dart:typed_data'; + import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; +const MethodCodec codec = JSONMethodCodec(); + +void emptyCallback(ByteData date) {} + TestLocationStrategy _strategy; TestLocationStrategy get strategy => _strategy; set strategy(TestLocationStrategy newStrategy) { @@ -17,7 +23,27 @@ void main() { strategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); expect(window.defaultRouteName, '/initial'); + // Changing the URL in the address bar later shouldn't affect [window.defaultRouteName]. strategy.replaceState(null, null, '/newpath'); expect(window.defaultRouteName, '/initial'); }); + + test('window.defaultRouteName should reset after navigation platform message', () { + strategy = TestLocationStrategy.fromEntry(TestHistoryEntry('initial state', null, '/initial')); + // Reading it multiple times should return the same value. + expect(window.defaultRouteName, '/initial'); + expect(window.defaultRouteName, '/initial'); + + window.sendPlatformMessage( + 'flutter/navigation', + JSONMethodCodec().encodeMethodCall(MethodCall( + 'routePushed', + {'previousRouteName': '/foo', 'routeName': '/bar'}, + )), + emptyCallback, + ); + // After a navigation platform message, [window.defaultRouteName] should + // reset to "/". + expect(window.defaultRouteName, '/'); + }); } From 7a591cf475876542804e4c0c12cdf307e28d1855 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Wed, 8 Apr 2020 14:48:56 -0700 Subject: [PATCH 320/386] [web] Fix regression of pointer events on mobile browsers (#17595) --- .../lib/src/engine/pointer_binding.dart | 2 +- .../test/engine/pointer_binding_test.dart | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index c4ed88afe2793..48e43dc72db45 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -196,7 +196,7 @@ abstract class _BaseAdapter { bool acceptOutsideGlasspane = false, }) { final html.EventListener loggedHandler = (html.Event event) { - if (!acceptOutsideGlasspane && event.target != glassPaneElement) { + if (!acceptOutsideGlasspane && !glassPaneElement.contains(event.target)) { return; } diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index dbe030d4910bf..3f8ed22b0968c 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -414,6 +414,75 @@ void main() { }, ); + _testEach<_ButtonedEventMixin>( + [_PointerEventContext(), _MouseEventContext()], + 'correctly detects events on the semantics placeholder', + (_ButtonedEventMixin context) { + PointerBinding.instance.debugOverrideDetector(context); + List packets = []; + ui.window.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + final html.Element semanticsPlaceholder = + html.Element.tag('flt-semantics-placeholder'); + glassPane.append(semanticsPlaceholder); + + // Press on the semantics placeholder. + semanticsPlaceholder.dispatchEvent(context.primaryDown( + clientX: 10.0, + clientY: 10.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].physicalX, equals(10.0)); + expect(packets[0].data[1].physicalY, equals(10.0)); + packets.clear(); + + // Drag on the semantics placeholder. + semanticsPlaceholder.dispatchEvent(context.primaryMove( + clientX: 12.0, + clientY: 10.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].physicalX, equals(12.0)); + expect(packets[0].data[0].physicalY, equals(10.0)); + packets.clear(); + + // Keep dragging. + semanticsPlaceholder.dispatchEvent(context.primaryMove( + clientX: 15.0, + clientY: 10.0, + )); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].physicalX, equals(15.0)); + expect(packets[0].data[0].physicalY, equals(10.0)); + packets.clear(); + + // Release the pointer on the semantics placeholder. + html.window.dispatchEvent(context.primaryUp( + clientX: 100.0, + clientY: 200.0, + )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].physicalX, equals(100.0)); + expect(packets[0].data[0].physicalY, equals(200.0)); + expect(packets[0].data[1].change, equals(ui.PointerChange.up)); + expect(packets[0].data[1].physicalX, equals(100.0)); + expect(packets[0].data[1].physicalY, equals(200.0)); + packets.clear(); + + semanticsPlaceholder.remove(); + }, + ); + // BUTTONED ADAPTERS _testEach<_ButtonedEventMixin>( From 0726621bfa653c7ebc7f069af455692005548310 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 8 Apr 2020 14:52:14 -0700 Subject: [PATCH 321/386] set -e on assemble_apk.sh (#17588) * set -e on assemble_apk.sh * build the necessary artifacts --- testing/scenario_app/assemble_apk.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testing/scenario_app/assemble_apk.sh b/testing/scenario_app/assemble_apk.sh index 9eb3cbfe7e0bc..3e75dcc5e9af4 100755 --- a/testing/scenario_app/assemble_apk.sh +++ b/testing/scenario_app/assemble_apk.sh @@ -1,5 +1,12 @@ #!/bin/bash +set -e + +pushd "${BASH_SOURCE%/*}/../../.." + ./flutter/tools/gn --unopt + ninja -C out/host_debug_unopt sky_engine sky_services +popd + pushd "${BASH_SOURCE%/*}" ./compile_android_aot.sh "$1" "$2" popd From db644677fa14fd64539ed09c58cb79a1f3fd1598 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Wed, 8 Apr 2020 17:18:06 -0700 Subject: [PATCH 322/386] [web] Fix multi-reply on message channel. Add test (#17591) * Fix multi-reply on message channel. Add test * Add check for viewInstanceCount * skip no-headless test --- .cirrus.yml | 1 + .../lib/platform_messages_main.dart | 64 +++++++++++++++++++ .../test_driver/platform_messages_e2e.dart | 59 +++++++++++++++++ .../platform_messages_e2e_test.dart | 7 ++ lib/web_ui/lib/src/engine/clipboard.dart | 10 ++- .../src/engine/compositor/embedded_views.dart | 2 - lib/web_ui/lib/src/engine/platform_views.dart | 2 - 7 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 e2etests/web/regular_integration_tests/lib/platform_messages_main.dart create mode 100644 e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e.dart create mode 100644 e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e_test.dart diff --git a/.cirrus.yml b/.cirrus.yml index b505c43f4c7de..d672e76ec8e93 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -172,6 +172,7 @@ task: - $FRAMEWORK_PATH/flutter/bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web - $FRAMEWORK_PATH/flutter/bin/flutter pub get --local-engine=host_debug_unopt - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/text_editing_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt + - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/platform_messages_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/treeshaking_e2e.dart -d web-server --profile --browser-name=chrome --local-engine=host_debug_unopt - $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/image_loading_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt diff --git a/e2etests/web/regular_integration_tests/lib/platform_messages_main.dart b/e2etests/web/regular_integration_tests/lib/platform_messages_main.dart new file mode 100644 index 0000000000000..4201da2b47b77 --- /dev/null +++ b/e2etests/web/regular_integration_tests/lib/platform_messages_main.dart @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() => runApp(MyApp()); + +Future dataFuture; + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + key: const Key('mainapp'), + title: 'Integration Test App For Platform Messages', + home: MyHomePage(title: 'Integration Test App For Platform Messages'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + final TextEditingController _controller = + TextEditingController(text: 'Text1'); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Hello World', + ), + // Create a text form field since we can't test clipboard unless + // html document has focus. + TextFormField( + key: const Key('input'), + enabled: true, + controller: _controller, + //initialValue: 'Text1', + decoration: const InputDecoration( + labelText: 'Text Input Field:', + ), + ), + ], + ), + ), + ); + } +} diff --git a/e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e.dart b/e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e.dart new file mode 100644 index 0000000000000..cd6a816b67f67 --- /dev/null +++ b/e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e.dart @@ -0,0 +1,59 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:html' as html; +// ignore: undefined_shown_name +import 'dart:ui' as ui show platformViewRegistry; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:regular_integration_tests/platform_messages_main.dart' as app; + +import 'package:e2e/e2e.dart'; + +void main() async { + E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding; + + testWidgets('platform message for Clipboard.setData reply with future', + (WidgetTester tester) async { + app.main(); + await tester.pumpAndSettle(); + + // TODO(nurhan): https://github.com/flutter/flutter/issues/51885 + SystemChannels.textInput.setMockMethodCallHandler(null); + // Focus on a TextFormField. + final Finder finder = find.byKey(const Key('input')); + expect(finder, findsOneWidget); + await tester.tap(find.byKey(const Key('input'))); + // Focus in input, otherwise clipboard will fail with + // 'document is not focused' platform exception. + html.document.querySelector('input').focus(); + await Clipboard.setData(const ClipboardData(text: 'sample text')); + }, skip: true); // https://github.com/flutter/flutter/issues/54296 + + testWidgets('Should create and dispose view embedder', + (WidgetTester tester) async { + int viewInstanceCount = 0; + + final int currentViewId = platformViewsRegistry.getNextPlatformViewId(); + // ignore: undefined_prefixed_name + ui.platformViewRegistry.registerViewFactory('MyView', (int viewId) { + ++viewInstanceCount; + return html.DivElement(); + }); + + app.main(); + await tester.pumpAndSettle(); + final Map createArgs = { + 'id': '567', + 'viewType': 'MyView', + }; + await SystemChannels.platform_views.invokeMethod('create', createArgs); + final Map disposeArgs = { + 'id': '567', + }; + await SystemChannels.platform_views.invokeMethod('dispose', disposeArgs); + expect(viewInstanceCount, 1); + }); +} diff --git a/e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e_test.dart b/e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e_test.dart new file mode 100644 index 0000000000000..a29203f7dcdd9 --- /dev/null +++ b/e2etests/web/regular_integration_tests/test_driver/platform_messages_e2e_test.dart @@ -0,0 +1,7 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:e2e/e2e_driver.dart' as e2e; + +Future main() async => e2e.main(); diff --git a/lib/web_ui/lib/src/engine/clipboard.dart b/lib/web_ui/lib/src/engine/clipboard.dart index de119a2842e73..cc5ed5532c96c 100644 --- a/lib/web_ui/lib/src/engine/clipboard.dart +++ b/lib/web_ui/lib/src/engine/clipboard.dart @@ -18,6 +18,7 @@ class ClipboardMessageHandler { void setDataMethodCall( MethodCall methodCall, ui.PlatformMessageResponseCallback callback) { const MethodCodec codec = JSONMethodCodec(); + bool errorEnvelopeEncoded = false; _copyToClipboardStrategy .setData(methodCall.arguments['text']) .then((bool success) { @@ -26,10 +27,15 @@ class ClipboardMessageHandler { } else { callback(codec.encodeErrorEnvelope( code: 'copy_fail', message: 'Clipboard.setData failed')); + errorEnvelopeEncoded = true; } }).catchError((dynamic _) { - callback(codec.encodeErrorEnvelope( - code: 'copy_fail', message: 'Clipboard.setData failed')); + // Don't encode a duplicate reply if we already failed and an error + // was already encoded. + if (!errorEnvelopeEncoded) { + callback(codec.encodeErrorEnvelope( + code: 'copy_fail', message: 'Clipboard.setData failed')); + } }); } diff --git a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart index 4cb6e64b7a74a..60d218d0d0e47 100644 --- a/lib/web_ui/lib/src/engine/compositor/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/compositor/embedded_views.dart @@ -69,11 +69,9 @@ class HtmlViewEmbedder { switch (decoded.method) { case 'create': _create(decoded, callback); - window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'dispose': _dispose(decoded, callback); - window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; } callback(null); diff --git a/lib/web_ui/lib/src/engine/platform_views.dart b/lib/web_ui/lib/src/engine/platform_views.dart index 2454cd44edb1d..d422f9a3ecf84 100644 --- a/lib/web_ui/lib/src/engine/platform_views.dart +++ b/lib/web_ui/lib/src/engine/platform_views.dart @@ -50,11 +50,9 @@ void handlePlatformViewCall( switch (decoded.method) { case 'create': _createPlatformView(decoded, callback); - window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; case 'dispose': _disposePlatformView(decoded, callback); - window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); return; } callback(null); From 5a78dd887442f085a9dd0e8aee88ac1a7164e28d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 8 Apr 2020 20:23:48 -0400 Subject: [PATCH 323/386] Roll fuchsia/sdk/core/mac-amd64 from wZ5qZ... to dCOo1... (#17576) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/fuchsia-mac-sdk-flutter-engine Please CC on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 94fd42a895289..9cec0f4e42655 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'wZ5qZ-VayHwkLFBdLl9lmjUt_CtxHjn9-oug0uWYB_MC' + 'version': 'dCOo1xXhC8Ozupm7VR8UF6XvdgcrLRO2ZmNzaZ3Jk3oC' } ], 'condition': 'host_os == "mac"', From f6b8eda714680e308a801d15829a5f71abee75d1 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 8 Apr 2020 17:33:33 -0700 Subject: [PATCH 324/386] Improve iOS PlatformViews to better handle thread merging. (#16935) --- ci/licenses_golden/licenses_flutter | 2 + flow/embedded_views.h | 12 ++++ shell/common/BUILD.gn | 2 + shell/common/animator_unittests.cc | 2 +- shell/common/rasterizer.cc | 10 +++ shell/common/shell_test.cc | 16 +++-- shell/common/shell_test.h | 10 ++- .../shell_test_external_view_embedder.cc | 55 ++++++++++++++ .../shell_test_external_view_embedder.h | 72 +++++++++++++++++++ shell/common/shell_test_platform_view.cc | 10 ++- shell/common/shell_test_platform_view.h | 5 +- shell/common/shell_test_platform_view_gl.cc | 9 ++- shell/common/shell_test_platform_view_gl.h | 8 ++- .../common/shell_test_platform_view_vulkan.cc | 23 ++++-- .../common/shell_test_platform_view_vulkan.h | 16 ++++- shell/common/shell_unittests.cc | 48 ++++++++++++- .../framework/Source/FlutterPlatformViews.mm | 30 +++++++- .../Source/FlutterPlatformViews_Internal.h | 12 ++++ shell/platform/darwin/ios/ios_surface.h | 3 + shell/platform/darwin/ios/ios_surface.mm | 28 +++++++- 20 files changed, 342 insertions(+), 31 deletions(-) create mode 100644 shell/common/shell_test_external_view_embedder.cc create mode 100644 shell/common/shell_test_external_view_embedder.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5c9e0ac454d9c..c05267758d76d 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -592,6 +592,8 @@ FILE: ../../../flutter/shell/common/shell_io_manager.cc FILE: ../../../flutter/shell/common/shell_io_manager.h FILE: ../../../flutter/shell/common/shell_test.cc FILE: ../../../flutter/shell/common/shell_test.h +FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.cc +FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.h FILE: ../../../flutter/shell/common/shell_test_platform_view.cc FILE: ../../../flutter/shell/common/shell_test_platform_view.h FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.cc diff --git a/flow/embedded_views.h b/flow/embedded_views.h index b80f7b4ba5d54..14dd00cf5e8c8 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -253,6 +253,18 @@ class ExternalViewEmbedder { // This is called after submitting the embedder frame and the surface frame. virtual void FinishFrame(); + // This should only be called after |SubmitFrame|. + // This method provides the embedder a way to do additional tasks after + // |SubmitFrame|. After invoking this method, the current task on the + // TaskRunner should end immediately. + // + // For example on the iOS embedder, threads are merged in this call. + // A new frame on the platform thread starts immediately. If the GPU thread + // still has some task running, there could be two frames being rendered + // concurrently, which causes undefined behaviors. + virtual void EndFrame( + fml::RefPtr raster_thread_merger) {} + FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); }; // ExternalViewEmbedder diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 66684679c3de3..3fab3317b094b 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -191,6 +191,8 @@ if (enable_unittests) { "pipeline_unittests.cc", "shell_test.cc", "shell_test.h", + "shell_test_external_view_embedder.cc", + "shell_test_external_view_embedder.h", "shell_test_platform_view.cc", "shell_test_platform_view.h", "shell_unittests.cc", diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index ed54ea5272be2..bf2ac9f5a9cf1 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -55,7 +55,7 @@ TEST_F(ShellTest, VSyncTargetTime) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index bce1b2bf036cb..ce456925c69ac 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -136,6 +136,16 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } + // Merging the thread as we know the next `Draw` should be run on the platform + // thread. + if (raster_status == RasterStatus::kResubmit) { + auto* external_view_embedder = surface_->GetExternalViewEmbedder(); + // We know only the `external_view_embedder` can + // causes|RasterStatus::kResubmit|. Check to make sure. + FML_DCHECK(external_view_embedder != nullptr); + external_view_embedder->EndFrame(raster_thread_merger_); + } + // Consume as many pipeline items as possible. But yield the event loop // between successive tries. switch (consume_result) { diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 378375c5eac1e..cf7f9ed8d801b 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -260,9 +260,12 @@ std::unique_ptr ShellTest::CreateShell(Settings settings, simulate_vsync); } -std::unique_ptr ShellTest::CreateShell(Settings settings, - TaskRunners task_runners, - bool simulate_vsync) { +std::unique_ptr ShellTest::CreateShell( + Settings settings, + TaskRunners task_runners, + bool simulate_vsync, + std::shared_ptr + shell_test_external_view_embedder) { const auto vsync_clock = std::make_shared(); CreateVsyncWaiter create_vsync_waiter = [&]() { if (simulate_vsync) { @@ -275,17 +278,18 @@ std::unique_ptr ShellTest::CreateShell(Settings settings, }; return Shell::Create( task_runners, settings, - [vsync_clock, &create_vsync_waiter](Shell& shell) { + [vsync_clock, &create_vsync_waiter, + shell_test_external_view_embedder](Shell& shell) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, + shell_test_external_view_embedder); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); }); } - void ShellTest::DestroyShell(std::unique_ptr shell) { DestroyShell(std::move(shell), GetTaskRunnersForFixture()); } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 680db7d349c1d..9f02aa35e68b3 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -15,6 +15,7 @@ #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiters_test.h" #include "flutter/testing/elf_loader.h" @@ -31,9 +32,12 @@ class ShellTest : public ThreadTest { Settings CreateSettingsForFixture(); std::unique_ptr CreateShell(Settings settings, bool simulate_vsync = false); - std::unique_ptr CreateShell(Settings settings, - TaskRunners task_runners, - bool simulate_vsync = false); + std::unique_ptr CreateShell( + Settings settings, + TaskRunners task_runners, + bool simulate_vsync = false, + std::shared_ptr + shell_test_external_view_embedder = nullptr); void DestroyShell(std::unique_ptr shell); void DestroyShell(std::unique_ptr shell, TaskRunners task_runners); TaskRunners GetTaskRunnersForFixture(); diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc new file mode 100644 index 0000000000000..683e21ad73c91 --- /dev/null +++ b/shell/common/shell_test_external_view_embedder.cc @@ -0,0 +1,55 @@ +#include "shell_test_external_view_embedder.h" + +namespace flutter { + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::CancelFrame() {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::BeginFrame(SkISize frame_size, + GrContext* context, + double device_pixel_ratio) {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) {} + +// |ExternalViewEmbedder| +PostPrerollResult ShellTestExternalViewEmbedder::PostPrerollAction( + fml::RefPtr raster_thread_merger) { + FML_DCHECK(raster_thread_merger); + return post_preroll_result_; +} + +// |ExternalViewEmbedder| +std::vector ShellTestExternalViewEmbedder::GetCurrentCanvases() { + return {}; +} + +// |ExternalViewEmbedder| +SkCanvas* ShellTestExternalViewEmbedder::CompositeEmbeddedView(int view_id) { + return nullptr; +} + +// |ExternalViewEmbedder| +bool ShellTestExternalViewEmbedder::SubmitFrame(GrContext* context, + SkCanvas* background_canvas) { + return true; +} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::FinishFrame() {} + +// |ExternalViewEmbedder| +void ShellTestExternalViewEmbedder::EndFrame( + fml::RefPtr raster_thread_merger) { + end_frame_call_back_(); +} + +// |ExternalViewEmbedder| +SkCanvas* ShellTestExternalViewEmbedder::GetRootCanvas() { + return nullptr; +} + +} // namespace flutter diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h new file mode 100644 index 0000000000000..56d9123ea8a7a --- /dev/null +++ b/shell/common/shell_test_external_view_embedder.h @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ +#define FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ + +#include "flutter/flow/embedded_views.h" +#include "flutter/fml/raster_thread_merger.h" + +namespace flutter { + +//------------------------------------------------------------------------------ +/// @brief The external view embedder used by |ShellTestPlatformViewGL| +/// +class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { + public: + using EndFrameCallBack = std::function; + + ShellTestExternalViewEmbedder(const EndFrameCallBack& end_frame_call_back, + PostPrerollResult post_preroll_result) + : end_frame_call_back_(end_frame_call_back), + post_preroll_result_(post_preroll_result) {} + + ~ShellTestExternalViewEmbedder() = default; + + private: + // |ExternalViewEmbedder| + void CancelFrame() override; + + // |ExternalViewEmbedder| + void BeginFrame(SkISize frame_size, + GrContext* context, + double device_pixel_ratio) override; + + // |ExternalViewEmbedder| + void PrerollCompositeEmbeddedView( + int view_id, + std::unique_ptr params) override; + + // |ExternalViewEmbedder| + PostPrerollResult PostPrerollAction( + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + std::vector GetCurrentCanvases() override; + + // |ExternalViewEmbedder| + SkCanvas* CompositeEmbeddedView(int view_id) override; + + // |ExternalViewEmbedder| + bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; + + // |ExternalViewEmbedder| + void FinishFrame() override; + + // |ExternalViewEmbedder| + void EndFrame( + fml::RefPtr raster_thread_merger) override; + + // |ExternalViewEmbedder| + SkCanvas* GetRootCanvas() override; + + const EndFrameCallBack end_frame_call_back_; + const PostPrerollResult post_preroll_result_; + + FML_DISALLOW_COPY_AND_ASSIGN(ShellTestExternalViewEmbedder); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ diff --git a/shell/common/shell_test_platform_view.cc b/shell/common/shell_test_platform_view.cc index cfd43308bfc10..29bd24d3134ff 100644 --- a/shell/common/shell_test_platform_view.cc +++ b/shell/common/shell_test_platform_view.cc @@ -19,7 +19,9 @@ std::unique_ptr ShellTestPlatformView::Create( TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend) { + BackendType backend, + std::shared_ptr + shell_test_external_view_embedder) { // TODO(gw280): https://github.com/flutter/flutter/issues/50298 // Make this fully runtime configurable switch (backend) { @@ -27,12 +29,14 @@ std::unique_ptr ShellTestPlatformView::Create( #ifdef SHELL_ENABLE_GL case BackendType::kGLBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter); + delegate, task_runners, vsync_clock, create_vsync_waiter, + shell_test_external_view_embedder); #endif // SHELL_ENABLE_GL #ifdef SHELL_ENABLE_VULKAN case BackendType::kVulkanBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter); + delegate, task_runners, vsync_clock, create_vsync_waiter, + shell_test_external_view_embedder); #endif // SHELL_ENABLE_VULKAN default: FML_LOG(FATAL) << "No backends supported for ShellTestPlatformView"; diff --git a/shell/common/shell_test_platform_view.h b/shell/common/shell_test_platform_view.h index c7aefe86b33bc..9a850b1d481d6 100644 --- a/shell/common/shell_test_platform_view.h +++ b/shell/common/shell_test_platform_view.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_H_ #include "flutter/shell/common/platform_view.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/vsync_waiters_test.h" namespace flutter { @@ -24,7 +25,9 @@ class ShellTestPlatformView : public PlatformView { TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend); + BackendType backend, + std::shared_ptr + shell_test_external_view_embedder); virtual void SimulateVSync() = 0; diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index 2bd597575e523..c980c53168645 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -12,11 +12,14 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter) + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder) : ShellTestPlatformView(delegate, std::move(task_runners)), gl_surface_(SkISize::Make(800, 600)), create_vsync_waiter_(std::move(create_vsync_waiter)), - vsync_clock_(vsync_clock) {} + vsync_clock_(vsync_clock), + shell_test_external_view_embedder_(shell_test_external_view_embedder) {} ShellTestPlatformViewGL::~ShellTestPlatformViewGL() = default; @@ -70,7 +73,7 @@ ShellTestPlatformViewGL::GetGLProcResolver() const { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* ShellTestPlatformViewGL::GetExternalViewEmbedder() { - return nullptr; + return shell_test_external_view_embedder_.get(); } } // namespace testing diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index 03db7910873e0..cac2fdc649781 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "flutter/testing/test_gl_surface.h" @@ -18,7 +19,9 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, ShellTestPlatformViewGL(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter); + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder); virtual ~ShellTestPlatformViewGL() override; @@ -31,6 +34,9 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, std::shared_ptr vsync_clock_; + std::shared_ptr + shell_test_external_view_embedder_; + // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index e9f41484d9881..4c21c2dfba36e 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -11,11 +11,14 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter) + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder) : ShellTestPlatformView(delegate, std::move(task_runners)), create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(vsync_clock), - proc_table_(fml::MakeRefCounted()) {} + proc_table_(fml::MakeRefCounted()), + shell_test_external_view_embedder_(shell_test_external_view_embedder) {} ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; @@ -29,7 +32,8 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface() { - return std::make_unique(proc_table_); + return std::make_unique(proc_table_, + shell_test_external_view_embedder_); } // |PlatformView| @@ -44,8 +48,12 @@ PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { // We need to merge this functionality back into //vulkan. // https://github.com/flutter/flutter/issues/51132 ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( - fml::RefPtr vk) - : valid_(false), vk_(std::move(vk)) { + fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder) + : valid_(false), + vk_(std::move(vk)), + shell_test_external_view_embedder_(shell_test_external_view_embedder) { if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; @@ -170,5 +178,10 @@ SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() return matrix; } +flutter::ExternalViewEmbedder* +ShellTestPlatformViewVulkan::OffScreenSurface::GetExternalViewEmbedder() { + return shell_test_external_view_embedder_.get(); +} + } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 38bce87fa5414..31757f39a30b0 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/vulkan/vulkan_application.h" @@ -18,7 +19,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { ShellTestPlatformViewVulkan(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter); + CreateVsyncWaiter create_vsync_waiter, + std::shared_ptr + shell_test_external_view_embedder); ~ShellTestPlatformViewVulkan() override; @@ -27,7 +30,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { private: class OffScreenSurface : public flutter::Surface { public: - OffScreenSurface(fml::RefPtr vk); + OffScreenSurface(fml::RefPtr vk, + std::shared_ptr + shell_test_external_view_embedder); ~OffScreenSurface() override; @@ -42,9 +47,13 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |Surface| GrContext* GetContext() override; + flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; + private: bool valid_; fml::RefPtr vk_; + std::shared_ptr + shell_test_external_view_embedder_; std::unique_ptr application_; std::unique_ptr logical_device_; sk_sp context_; @@ -61,6 +70,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { fml::RefPtr proc_table_; + std::shared_ptr + shell_test_external_view_embedder_; + // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index c9c1345541fcc..720666ae1bb69 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -23,6 +23,7 @@ #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_test.h" +#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" @@ -139,7 +140,7 @@ TEST_F(ShellTest, return static_cast>( std::make_unique(task_runners)); }, - ShellTestPlatformView::BackendType::kDefaultBackend); + ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); @@ -466,6 +467,51 @@ TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { DestroyShell(std::move(shell)); } +TEST_F(ShellTest, + ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit) { + auto settings = CreateSettingsForFixture(); + fml::AutoResetWaitableEvent endFrameLatch; + bool end_frame_called = false; + auto end_frame_callback = [&] { + end_frame_called = true; + endFrameLatch.Signal(); + }; + auto external_view_embedder = std::make_shared( + end_frame_callback, PostPrerollResult::kResubmitFrame); + auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), + false, external_view_embedder); + + // Create the surface needed by rasterizer + PlatformViewNotifyCreated(shell.get()); + + auto configuration = RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("emptyMain"); + + RunEngine(shell.get(), std::move(configuration)); + + LayerTreeBuilder builder = [&](std::shared_ptr root) { + SkPictureRecorder recorder; + SkCanvas* recording_canvas = + recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80)); + recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80), + SkPaint(SkColor4f::FromColor(SK_ColorRED))); + auto sk_picture = recorder.finishRecordingAsPicture(); + fml::RefPtr queue = fml::MakeRefCounted( + this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); + auto picture_layer = std::make_shared( + SkPoint::Make(10, 10), + flutter::SkiaGPUObject({sk_picture, queue}), false, false); + root->Add(picture_layer); + }; + + PumpOneFrame(shell.get(), 100, 100, builder); + endFrameLatch.Wait(); + + ASSERT_TRUE(end_frame_called); + + DestroyShell(std::move(shell)); +} + TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { // Ensure that all phases are in kPhases. ASSERT_EQ(sizeof(FrameTiming::kPhases), diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 3fbaa7dd8a935..2c2a91f4836a6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -248,7 +248,7 @@ } void FlutterPlatformViewsController::CancelFrame() { - composition_order_.clear(); + composition_order_ = active_composition_order_; } bool FlutterPlatformViewsController::HasPendingViewOperations() { @@ -267,8 +267,9 @@ if (raster_thread_merger->IsMerged()) { raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); } else { + // Wait until |EndFrame| to merge the threads. + merge_threads_ = true; CancelFrame(); - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); return PostPrerollResult::kResubmitFrame; } } @@ -468,6 +469,21 @@ bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context, SkCanvas* background_canvas) { + if (merge_threads_) { + // Threads are about to be merged, we drop everything from this frame + // and possibly resubmit the same layer tree in the next frame. + // Before merging thread, we know the code is not running on the main thread. Assert that + FML_DCHECK(![[NSThread currentThread] isMainThread]); + picture_recorders_.clear(); + composition_order_.clear(); + return true; + } + + // Any UIKit related code has to run on main thread. + // When on a non-main thread, we only allow the rest of the method to run if there is no platform + // view. + FML_DCHECK([[NSThread currentThread] isMainThread] || views_to_dispose_.empty()); + DisposeViews(); // Resolve all pending GPU operations before allocating a new surface. @@ -578,6 +594,14 @@ } } +void FlutterPlatformViewsController::EndFrame( + fml::RefPtr raster_thread_merger) { + if (merge_threads_) { + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); + merge_threads_ = false; + } +} + std::shared_ptr FlutterPlatformViewsController::GetLayer( GrContext* gr_context, std::shared_ptr ios_context, @@ -642,6 +666,8 @@ return; } + FML_DCHECK([[NSThread currentThread] isMainThread]); + for (int64_t viewId : views_to_dispose_) { UIView* root_view = root_views_[viewId].get(); [root_view removeFromSuperview]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index e998640777c96..6532be8ca14c2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -134,6 +134,8 @@ class FlutterPlatformViewsController { void SetFrameSize(SkISize frame_size); + // Indicates that we don't compisite any platform views or overlays during this frame. + // Also reverts the composition_order_ to its original state at the begining of the frame. void CancelFrame(); void PrerollCompositeEmbeddedView(int view_id, @@ -163,6 +165,11 @@ class FlutterPlatformViewsController { std::shared_ptr ios_context, SkCanvas* background_canvas); + // Invoked at the very end of a frame. + // After invoking this method, nothing should happen on the current TaskRunner during the same + // frame. + void EndFrame(fml::RefPtr raster_thread_merger); + void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: @@ -273,6 +280,10 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); + // Default to `false`. + // If `true`, gpu thread and platform thread should be merged during |EndFrame|. + // Always resets to `false` right after the threads are merged. + bool merge_threads_; // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from // the picture on the layer's canvas. std::shared_ptr GetLayer(GrContext* gr_context, @@ -282,6 +293,7 @@ class FlutterPlatformViewsController { int64_t view_id, int64_t overlay_id); // Removes overlay views and platform views that aren't needed in the current frame. + // Must run on the platform thread. void RemoveUnusedLayers(); // Appends the overlay views and platform view and sets their z index based on the composition // order. diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index fd1bc7abb0b48..eae39031d77ce 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -83,6 +83,9 @@ class IOSSurface : public ExternalViewEmbedder { // |ExternalViewEmbedder| void FinishFrame() override; + // |ExternalViewEmbedder| + void EndFrame(fml::RefPtr raster_thread_merger) override; + public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); }; diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index cbe2cb4fb8c2c..5f2725273e0dd 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -90,7 +90,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { platform_views_controller_->CancelFrame(); // Committing the current transaction as |BeginFrame| will create a nested // CATransaction otherwise. - [CATransaction commit]; + if ([[NSThread currentThread] isMainThread]) { + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. + [CATransaction commit]; + } } // |ExternalViewEmbedder| @@ -98,7 +103,12 @@ bool IsIosEmbeddedViewsPreviewEnabled() { TRACE_EVENT0("flutter", "IOSSurface::BeginFrame"); FML_CHECK(platform_views_controller_ != nullptr); platform_views_controller_->SetFrameSize(frame_size); - [CATransaction begin]; + if ([[NSThread currentThread] isMainThread]) { + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. + [CATransaction begin]; + } } // |ExternalViewEmbedder| @@ -140,10 +150,22 @@ bool IsIosEmbeddedViewsPreviewEnabled() { return submitted; } +// |ExternalViewEmbedder| +void IOSSurface::EndFrame(fml::RefPtr raster_thread_merger) { + TRACE_EVENT0("flutter", "IOSSurface::EndFrame"); + FML_CHECK(platform_views_controller_ != nullptr); + return platform_views_controller_->EndFrame(raster_thread_merger); +} + // |ExternalViewEmbedder| void IOSSurface::FinishFrame() { TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); + if (![[NSThread currentThread] isMainThread]) { + return; + } + // The only time we need to commit the `CATranscation` is when + // there are platform views in the scene, which has to be run on the + // main thread. [CATransaction commit]; } - } // namespace flutter From 6245149b41cadfe06507ff13d1b766b2c37f745c Mon Sep 17 00:00:00 2001 From: Nurhan Turgut Date: Wed, 8 Apr 2020 17:35:10 -0700 Subject: [PATCH 325/386] [web] using pkill to quit safari (#17533) * adding an apple script to quit safari * making changes to utils and adding cleanup after unit tests failed * switch saai quit to pkill instead of applescript * adding toolexception * adding a cleanup function option to felt. * removing unnecassary await's added for the previos version of the PR * removing unnecassary async * fix analyze error --- lib/web_ui/dev/chrome_installer.dart | 1 + lib/web_ui/dev/common.dart | 29 ------------------ lib/web_ui/dev/environment.dart | 5 ++-- lib/web_ui/dev/exceptions.dart | 30 +++++++++++++++++++ lib/web_ui/dev/felt.dart | 43 +++++++++++++-------------- lib/web_ui/dev/firefox_installer.dart | 1 + lib/web_ui/dev/test_runner.dart | 39 +++++++++++++++++------- lib/web_ui/dev/utils.dart | 43 +++++++++++++++++++++++++-- 8 files changed, 124 insertions(+), 67 deletions(-) create mode 100644 lib/web_ui/dev/exceptions.dart diff --git a/lib/web_ui/dev/chrome_installer.dart b/lib/web_ui/dev/chrome_installer.dart index b6345ea379858..48e10def23fa8 100644 --- a/lib/web_ui/dev/chrome_installer.dart +++ b/lib/web_ui/dev/chrome_installer.dart @@ -13,6 +13,7 @@ import 'package:yaml/yaml.dart'; import 'common.dart'; import 'environment.dart'; +import 'exceptions.dart'; class ChromeArgParser extends BrowserArgParser { static final ChromeArgParser _singletonInstance = ChromeArgParser._(); diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index 3939bf2e9b6cd..344316be76511 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -18,24 +18,6 @@ const int kMaxScreenshotWidth = 1024; const int kMaxScreenshotHeight = 1024; const double kMaxDiffRateFailure = 0.28 / 100; // 0.28% -class BrowserInstallerException implements Exception { - BrowserInstallerException(this.message); - - final String message; - - @override - String toString() => message; -} - -class DriverException implements Exception { - DriverException(this.message); - - final String message; - - @override - String toString() => message; -} - abstract class PlatformBinding { static PlatformBinding get instance { if (_instance == null) { @@ -250,14 +232,3 @@ class DevNull implements StringSink { } bool get isCirrus => io.Platform.environment['CIRRUS_CI'] == 'true'; - -/// There might be proccesses started during the tests. -/// -/// Use this list to store those Processes, for cleaning up before shutdown. -final List processesToCleanUp = List(); - -/// There might be temporary directories created during the tests. -/// -/// Use this list to store those directories and for deleteing them before -/// shutdown. -final List temporaryDirectories = List(); diff --git a/lib/web_ui/dev/environment.dart b/lib/web_ui/dev/environment.dart index 6af6e6e874271..3214c8a083b3d 100644 --- a/lib/web_ui/dev/environment.dart +++ b/lib/web_ui/dev/environment.dart @@ -6,6 +6,8 @@ import 'dart:io' as io; import 'package:path/path.dart' as pathlib; +import 'exceptions.dart'; + /// Contains various environment variables, such as common file paths and command-line options. Environment get environment { _environment ??= Environment(); @@ -26,8 +28,7 @@ class Environment { for (io.Directory expectedDirectory in [engineSrcDir, outDir, hostDebugUnoptDir, dartSdkDir, webUiRootDir]) { if (!expectedDirectory.existsSync()) { - io.stderr.writeln('$expectedDirectory does not exist.'); - io.exit(1); + throw ToolException('$expectedDirectory does not exist.'); } } diff --git a/lib/web_ui/dev/exceptions.dart b/lib/web_ui/dev/exceptions.dart new file mode 100644 index 0000000000000..167d1734e655f --- /dev/null +++ b/lib/web_ui/dev/exceptions.dart @@ -0,0 +1,30 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +class BrowserInstallerException implements Exception { + BrowserInstallerException(this.message); + + final String message; + + @override + String toString() => message; +} + +class DriverException implements Exception { + DriverException(this.message); + + final String message; + + @override + String toString() => message; +} + +class ToolException implements Exception { + ToolException(this.message); + + final String message; + + @override + String toString() => message; +} diff --git a/lib/web_ui/dev/felt.dart b/lib/web_ui/dev/felt.dart index 3ba2984777d65..32f3619dc7014 100644 --- a/lib/web_ui/dev/felt.dart +++ b/lib/web_ui/dev/felt.dart @@ -9,9 +9,10 @@ import 'package:args/command_runner.dart'; import 'build.dart'; import 'clean.dart'; -import 'common.dart'; import 'licenses.dart'; +import 'exceptions.dart'; import 'test_runner.dart'; +import 'utils.dart'; CommandRunner runner = CommandRunner( 'felt', @@ -31,50 +32,46 @@ void main(List args) async { _listenToShutdownSignals(); + int exitCode = -1; try { final bool result = (await runner.run(args)) as bool; if (result == false) { print('Sub-command returned false: `${args.join(' ')}`'); - _cleanup(); - io.exit(1); + await cleanup(); + exitCode = 1; } } on UsageException catch (e) { print(e); - io.exit(64); // Exit code 64 indicates a usage error. + exitCode = 64; // Exit code 64 indicates a usage error. + } on ToolException catch (e) { + io.stderr.writeln(e.message); + exitCode = 1; } catch (e) { rethrow; } finally { - _cleanup(); + await cleanup(); + // The exit code is changed by one of the branches. + if(exitCode != -1) { + io.exit(exitCode); + } } // Sometimes the Dart VM refuses to quit. io.exit(io.exitCode); } -void _cleanup() { - // Cleanup remaining processes if any. - if (processesToCleanUp.length > 0) { - for (io.Process process in processesToCleanUp) { - process.kill(); - } - } - // Delete temporary directories. - if (temporaryDirectories.length > 0) { - for (io.Directory directory in temporaryDirectories) { - directory.deleteSync(recursive: true); - } - } -} - -void _listenToShutdownSignals() { - io.ProcessSignal.sigint.watch().listen((_) { +void _listenToShutdownSignals() async { + io.ProcessSignal.sigint.watch().listen((_) async { print('Received SIGINT. Shutting down.'); + await cleanup(); io.exit(1); }); + // SIGTERM signals are not generated under Windows. // See https://docs.microsoft.com/en-us/previous-versions/xdkz3x12(v%3Dvs.140) if (!io.Platform.isWindows) { - io.ProcessSignal.sigterm.watch().listen((_) { + io.ProcessSignal.sigterm.watch().listen((_) async { + await cleanup(); print('Received SIGTERM. Shutting down.'); io.exit(1); }); diff --git a/lib/web_ui/dev/firefox_installer.dart b/lib/web_ui/dev/firefox_installer.dart index 5a5c951c719dd..e282d2a3df23f 100644 --- a/lib/web_ui/dev/firefox_installer.dart +++ b/lib/web_ui/dev/firefox_installer.dart @@ -12,6 +12,7 @@ import 'package:yaml/yaml.dart'; import 'common.dart'; import 'environment.dart'; +import 'exceptions.dart'; class FirefoxArgParser extends BrowserArgParser { static final FirefoxArgParser _singletonInstance = FirefoxArgParser._(); diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 5f5995c3d6465..5b62a064d7b8d 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -15,6 +15,7 @@ import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_im import 'package:test_core/src/executable.dart' as test; // ignore: implementation_imports +import 'exceptions.dart'; import 'integration_tests_manager.dart'; import 'supported_browsers.dart'; import 'test_platform.dart'; @@ -151,6 +152,26 @@ class TestCommand extends Command with ArgUtils { } await _buildTests(targets: targetFiles); + + // Many tabs will be left open after Safari runs, quit Safari during + // cleanup. + if (browser == 'safari') { + cleanupCallbacks.add(() async { + // Only close Safari if felt is running in CI environments. Do not close + // Safari for the local testing. + if (io.Platform.environment['LUCI_CONTEXT'] != null || isCirrus) { + print('INFO: Safari tests ran. Quit Safari.'); + await runProcess( + 'sudo', + ['pkill', '-lf', 'Safari'], + workingDirectory: environment.webUiRootDir.path, + ); + } else { + print('INFO: Safari tests ran. Please quit Safari tabs.'); + } + }); + } + if (runAllTests) { await _runAllTests(); } else { @@ -179,7 +200,7 @@ class TestCommand extends Command with ArgUtils { bool get runAllTests => targets.isEmpty; /// The name of the browser to run tests in. - String get browser => stringArg('browser'); + String get browser => (argResults != null) ? stringArg('browser') : 'chrome'; /// Whether [browser] is set to "chrome". bool get isChrome => browser == 'chrome'; @@ -275,8 +296,7 @@ class TestCommand extends Command with ArgUtils { void _checkExitCode() { if (io.exitCode != 0) { - io.stderr.writeln('Process exited with exit code ${io.exitCode}.'); - io.exit(1); + throw ToolException('Process exited with exit code ${io.exitCode}.'); } } @@ -290,9 +310,8 @@ class TestCommand extends Command with ArgUtils { ); if (exitCode != 0) { - io.stderr - .writeln('Failed to run pub get. Exited with exit code $exitCode'); - io.exit(1); + throw ToolException( + 'Failed to run pub get. Exited with exit code $exitCode'); } } @@ -333,9 +352,8 @@ class TestCommand extends Command with ArgUtils { ); if (exitCode != 0) { - io.stderr.writeln( - 'Failed to compile ${hostDartFile.path}. Compiler exited with exit code $exitCode'); - io.exit(1); + throw ToolException('Failed to compile ${hostDartFile.path}. Compiler ' + 'exited with exit code $exitCode'); } // Record the timestamp to avoid rebuilding unless the file changes. @@ -363,9 +381,8 @@ class TestCommand extends Command with ArgUtils { ); if (exitCode != 0) { - io.stderr.writeln( + throw ToolException( 'Failed to compile tests. Compiler exited with exit code $exitCode'); - io.exit(1); } } diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index 4cf53b6651a62..bf3475e8874fc 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -10,7 +10,6 @@ import 'package:args/command_runner.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'common.dart'; import 'environment.dart'; class FilePath { @@ -130,7 +129,8 @@ class ProcessException implements Exception { message ..writeln(description) ..writeln('Command: $executable ${arguments.join(' ')}') - ..writeln('Working directory: ${workingDirectory ?? io.Directory.current.path}') + ..writeln( + 'Working directory: ${workingDirectory ?? io.Directory.current.path}') ..writeln('Exit code: $exitCode'); return '$message'; } @@ -161,3 +161,42 @@ mixin ArgUtils on Command { return value; } } + +/// There might be proccesses started during the tests. +/// +/// Use this list to store those Processes, for cleaning up before shutdown. +final List processesToCleanUp = List(); + +/// There might be temporary directories created during the tests. +/// +/// Use this list to store those directories and for deleteing them before +/// shutdown. +final List temporaryDirectories = List(); + +typedef AsyncCallback = Future Function(); + +/// There might be additional cleanup needs to be done after the tools ran. +/// +/// Add these operations here to make sure that they will run before felt +/// exit. +final List cleanupCallbacks = List(); + +/// Cleanup the remaning processes, close open browsers, delete temp files. +void cleanup() async { + // Cleanup remaining processes if any. + if (processesToCleanUp.length > 0) { + for (io.Process process in processesToCleanUp) { + process.kill(); + } + } + // Delete temporary directories. + if (temporaryDirectories.length > 0) { + for (io.Directory directory in temporaryDirectories) { + directory.deleteSync(recursive: true); + } + } + + cleanupCallbacks.forEach((element) { + element.call(); + }); +} From caebc93e0c4bfb8be278cf89a827229340745706 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Wed, 8 Apr 2020 21:15:38 -0700 Subject: [PATCH 326/386] Revert "Improve iOS PlatformViews to better handle thread merging. (#16935)" (#17600) This reverts commit f6b8eda714680e308a801d15829a5f71abee75d1. --- ci/licenses_golden/licenses_flutter | 2 - flow/embedded_views.h | 12 ---- shell/common/BUILD.gn | 2 - shell/common/animator_unittests.cc | 2 +- shell/common/rasterizer.cc | 10 --- shell/common/shell_test.cc | 16 ++--- shell/common/shell_test.h | 10 +-- .../shell_test_external_view_embedder.cc | 55 -------------- .../shell_test_external_view_embedder.h | 72 ------------------- shell/common/shell_test_platform_view.cc | 10 +-- shell/common/shell_test_platform_view.h | 5 +- shell/common/shell_test_platform_view_gl.cc | 9 +-- shell/common/shell_test_platform_view_gl.h | 8 +-- .../common/shell_test_platform_view_vulkan.cc | 23 ++---- .../common/shell_test_platform_view_vulkan.h | 16 +---- shell/common/shell_unittests.cc | 48 +------------ .../framework/Source/FlutterPlatformViews.mm | 30 +------- .../Source/FlutterPlatformViews_Internal.h | 12 ---- shell/platform/darwin/ios/ios_surface.h | 3 - shell/platform/darwin/ios/ios_surface.mm | 28 +------- 20 files changed, 31 insertions(+), 342 deletions(-) delete mode 100644 shell/common/shell_test_external_view_embedder.cc delete mode 100644 shell/common/shell_test_external_view_embedder.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c05267758d76d..5c9e0ac454d9c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -592,8 +592,6 @@ FILE: ../../../flutter/shell/common/shell_io_manager.cc FILE: ../../../flutter/shell/common/shell_io_manager.h FILE: ../../../flutter/shell/common/shell_test.cc FILE: ../../../flutter/shell/common/shell_test.h -FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.cc -FILE: ../../../flutter/shell/common/shell_test_external_view_embedder.h FILE: ../../../flutter/shell/common/shell_test_platform_view.cc FILE: ../../../flutter/shell/common/shell_test_platform_view.h FILE: ../../../flutter/shell/common/shell_test_platform_view_gl.cc diff --git a/flow/embedded_views.h b/flow/embedded_views.h index 14dd00cf5e8c8..b80f7b4ba5d54 100644 --- a/flow/embedded_views.h +++ b/flow/embedded_views.h @@ -253,18 +253,6 @@ class ExternalViewEmbedder { // This is called after submitting the embedder frame and the surface frame. virtual void FinishFrame(); - // This should only be called after |SubmitFrame|. - // This method provides the embedder a way to do additional tasks after - // |SubmitFrame|. After invoking this method, the current task on the - // TaskRunner should end immediately. - // - // For example on the iOS embedder, threads are merged in this call. - // A new frame on the platform thread starts immediately. If the GPU thread - // still has some task running, there could be two frames being rendered - // concurrently, which causes undefined behaviors. - virtual void EndFrame( - fml::RefPtr raster_thread_merger) {} - FML_DISALLOW_COPY_AND_ASSIGN(ExternalViewEmbedder); }; // ExternalViewEmbedder diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 3fab3317b094b..66684679c3de3 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -191,8 +191,6 @@ if (enable_unittests) { "pipeline_unittests.cc", "shell_test.cc", "shell_test.h", - "shell_test_external_view_embedder.cc", - "shell_test_external_view_embedder.h", "shell_test_platform_view.cc", "shell_test_platform_view.h", "shell_unittests.cc", diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index bf2ac9f5a9cf1..ed54ea5272be2 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -55,7 +55,7 @@ TEST_F(ShellTest, VSyncTargetTime) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); + ShellTestPlatformView::BackendType::kDefaultBackend); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index ce456925c69ac..bce1b2bf036cb 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -136,16 +136,6 @@ void Rasterizer::Draw(fml::RefPtr> pipeline) { consume_result = PipelineConsumeResult::MoreAvailable; } - // Merging the thread as we know the next `Draw` should be run on the platform - // thread. - if (raster_status == RasterStatus::kResubmit) { - auto* external_view_embedder = surface_->GetExternalViewEmbedder(); - // We know only the `external_view_embedder` can - // causes|RasterStatus::kResubmit|. Check to make sure. - FML_DCHECK(external_view_embedder != nullptr); - external_view_embedder->EndFrame(raster_thread_merger_); - } - // Consume as many pipeline items as possible. But yield the event loop // between successive tries. switch (consume_result) { diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index cf7f9ed8d801b..378375c5eac1e 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -260,12 +260,9 @@ std::unique_ptr ShellTest::CreateShell(Settings settings, simulate_vsync); } -std::unique_ptr ShellTest::CreateShell( - Settings settings, - TaskRunners task_runners, - bool simulate_vsync, - std::shared_ptr - shell_test_external_view_embedder) { +std::unique_ptr ShellTest::CreateShell(Settings settings, + TaskRunners task_runners, + bool simulate_vsync) { const auto vsync_clock = std::make_shared(); CreateVsyncWaiter create_vsync_waiter = [&]() { if (simulate_vsync) { @@ -278,18 +275,17 @@ std::unique_ptr ShellTest::CreateShell( }; return Shell::Create( task_runners, settings, - [vsync_clock, &create_vsync_waiter, - shell_test_external_view_embedder](Shell& shell) { + [vsync_clock, &create_vsync_waiter](Shell& shell) { return ShellTestPlatformView::Create( shell, shell.GetTaskRunners(), vsync_clock, std::move(create_vsync_waiter), - ShellTestPlatformView::BackendType::kDefaultBackend, - shell_test_external_view_embedder); + ShellTestPlatformView::BackendType::kDefaultBackend); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); }); } + void ShellTest::DestroyShell(std::unique_ptr shell) { DestroyShell(std::move(shell), GetTaskRunnersForFixture()); } diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 9f02aa35e68b3..680db7d349c1d 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -15,7 +15,6 @@ #include "flutter/lib/ui/window/platform_message.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell.h" -#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiters_test.h" #include "flutter/testing/elf_loader.h" @@ -32,12 +31,9 @@ class ShellTest : public ThreadTest { Settings CreateSettingsForFixture(); std::unique_ptr CreateShell(Settings settings, bool simulate_vsync = false); - std::unique_ptr CreateShell( - Settings settings, - TaskRunners task_runners, - bool simulate_vsync = false, - std::shared_ptr - shell_test_external_view_embedder = nullptr); + std::unique_ptr CreateShell(Settings settings, + TaskRunners task_runners, + bool simulate_vsync = false); void DestroyShell(std::unique_ptr shell); void DestroyShell(std::unique_ptr shell, TaskRunners task_runners); TaskRunners GetTaskRunnersForFixture(); diff --git a/shell/common/shell_test_external_view_embedder.cc b/shell/common/shell_test_external_view_embedder.cc deleted file mode 100644 index 683e21ad73c91..0000000000000 --- a/shell/common/shell_test_external_view_embedder.cc +++ /dev/null @@ -1,55 +0,0 @@ -#include "shell_test_external_view_embedder.h" - -namespace flutter { - -// |ExternalViewEmbedder| -void ShellTestExternalViewEmbedder::CancelFrame() {} - -// |ExternalViewEmbedder| -void ShellTestExternalViewEmbedder::BeginFrame(SkISize frame_size, - GrContext* context, - double device_pixel_ratio) {} - -// |ExternalViewEmbedder| -void ShellTestExternalViewEmbedder::PrerollCompositeEmbeddedView( - int view_id, - std::unique_ptr params) {} - -// |ExternalViewEmbedder| -PostPrerollResult ShellTestExternalViewEmbedder::PostPrerollAction( - fml::RefPtr raster_thread_merger) { - FML_DCHECK(raster_thread_merger); - return post_preroll_result_; -} - -// |ExternalViewEmbedder| -std::vector ShellTestExternalViewEmbedder::GetCurrentCanvases() { - return {}; -} - -// |ExternalViewEmbedder| -SkCanvas* ShellTestExternalViewEmbedder::CompositeEmbeddedView(int view_id) { - return nullptr; -} - -// |ExternalViewEmbedder| -bool ShellTestExternalViewEmbedder::SubmitFrame(GrContext* context, - SkCanvas* background_canvas) { - return true; -} - -// |ExternalViewEmbedder| -void ShellTestExternalViewEmbedder::FinishFrame() {} - -// |ExternalViewEmbedder| -void ShellTestExternalViewEmbedder::EndFrame( - fml::RefPtr raster_thread_merger) { - end_frame_call_back_(); -} - -// |ExternalViewEmbedder| -SkCanvas* ShellTestExternalViewEmbedder::GetRootCanvas() { - return nullptr; -} - -} // namespace flutter diff --git a/shell/common/shell_test_external_view_embedder.h b/shell/common/shell_test_external_view_embedder.h deleted file mode 100644 index 56d9123ea8a7a..0000000000000 --- a/shell/common/shell_test_external_view_embedder.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ -#define FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ - -#include "flutter/flow/embedded_views.h" -#include "flutter/fml/raster_thread_merger.h" - -namespace flutter { - -//------------------------------------------------------------------------------ -/// @brief The external view embedder used by |ShellTestPlatformViewGL| -/// -class ShellTestExternalViewEmbedder final : public ExternalViewEmbedder { - public: - using EndFrameCallBack = std::function; - - ShellTestExternalViewEmbedder(const EndFrameCallBack& end_frame_call_back, - PostPrerollResult post_preroll_result) - : end_frame_call_back_(end_frame_call_back), - post_preroll_result_(post_preroll_result) {} - - ~ShellTestExternalViewEmbedder() = default; - - private: - // |ExternalViewEmbedder| - void CancelFrame() override; - - // |ExternalViewEmbedder| - void BeginFrame(SkISize frame_size, - GrContext* context, - double device_pixel_ratio) override; - - // |ExternalViewEmbedder| - void PrerollCompositeEmbeddedView( - int view_id, - std::unique_ptr params) override; - - // |ExternalViewEmbedder| - PostPrerollResult PostPrerollAction( - fml::RefPtr raster_thread_merger) override; - - // |ExternalViewEmbedder| - std::vector GetCurrentCanvases() override; - - // |ExternalViewEmbedder| - SkCanvas* CompositeEmbeddedView(int view_id) override; - - // |ExternalViewEmbedder| - bool SubmitFrame(GrContext* context, SkCanvas* background_canvas) override; - - // |ExternalViewEmbedder| - void FinishFrame() override; - - // |ExternalViewEmbedder| - void EndFrame( - fml::RefPtr raster_thread_merger) override; - - // |ExternalViewEmbedder| - SkCanvas* GetRootCanvas() override; - - const EndFrameCallBack end_frame_call_back_; - const PostPrerollResult post_preroll_result_; - - FML_DISALLOW_COPY_AND_ASSIGN(ShellTestExternalViewEmbedder); -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_TEST_EXTERNAL_VIEW_EMBEDDER_H_ diff --git a/shell/common/shell_test_platform_view.cc b/shell/common/shell_test_platform_view.cc index 29bd24d3134ff..cfd43308bfc10 100644 --- a/shell/common/shell_test_platform_view.cc +++ b/shell/common/shell_test_platform_view.cc @@ -19,9 +19,7 @@ std::unique_ptr ShellTestPlatformView::Create( TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend, - std::shared_ptr - shell_test_external_view_embedder) { + BackendType backend) { // TODO(gw280): https://github.com/flutter/flutter/issues/50298 // Make this fully runtime configurable switch (backend) { @@ -29,14 +27,12 @@ std::unique_ptr ShellTestPlatformView::Create( #ifdef SHELL_ENABLE_GL case BackendType::kGLBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter, - shell_test_external_view_embedder); + delegate, task_runners, vsync_clock, create_vsync_waiter); #endif // SHELL_ENABLE_GL #ifdef SHELL_ENABLE_VULKAN case BackendType::kVulkanBackend: return std::make_unique( - delegate, task_runners, vsync_clock, create_vsync_waiter, - shell_test_external_view_embedder); + delegate, task_runners, vsync_clock, create_vsync_waiter); #endif // SHELL_ENABLE_VULKAN default: FML_LOG(FATAL) << "No backends supported for ShellTestPlatformView"; diff --git a/shell/common/shell_test_platform_view.h b/shell/common/shell_test_platform_view.h index 9a850b1d481d6..c7aefe86b33bc 100644 --- a/shell/common/shell_test_platform_view.h +++ b/shell/common/shell_test_platform_view.h @@ -6,7 +6,6 @@ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_H_ #include "flutter/shell/common/platform_view.h" -#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/vsync_waiters_test.h" namespace flutter { @@ -25,9 +24,7 @@ class ShellTestPlatformView : public PlatformView { TaskRunners task_runners, std::shared_ptr vsync_clock, CreateVsyncWaiter create_vsync_waiter, - BackendType backend, - std::shared_ptr - shell_test_external_view_embedder); + BackendType backend); virtual void SimulateVSync() = 0; diff --git a/shell/common/shell_test_platform_view_gl.cc b/shell/common/shell_test_platform_view_gl.cc index c980c53168645..2bd597575e523 100644 --- a/shell/common/shell_test_platform_view_gl.cc +++ b/shell/common/shell_test_platform_view_gl.cc @@ -12,14 +12,11 @@ ShellTestPlatformViewGL::ShellTestPlatformViewGL( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter, - std::shared_ptr - shell_test_external_view_embedder) + CreateVsyncWaiter create_vsync_waiter) : ShellTestPlatformView(delegate, std::move(task_runners)), gl_surface_(SkISize::Make(800, 600)), create_vsync_waiter_(std::move(create_vsync_waiter)), - vsync_clock_(vsync_clock), - shell_test_external_view_embedder_(shell_test_external_view_embedder) {} + vsync_clock_(vsync_clock) {} ShellTestPlatformViewGL::~ShellTestPlatformViewGL() = default; @@ -73,7 +70,7 @@ ShellTestPlatformViewGL::GetGLProcResolver() const { // |GPUSurfaceGLDelegate| ExternalViewEmbedder* ShellTestPlatformViewGL::GetExternalViewEmbedder() { - return shell_test_external_view_embedder_.get(); + return nullptr; } } // namespace testing diff --git a/shell/common/shell_test_platform_view_gl.h b/shell/common/shell_test_platform_view_gl.h index cac2fdc649781..03db7910873e0 100644 --- a/shell/common/shell_test_platform_view_gl.h +++ b/shell/common/shell_test_platform_view_gl.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_GL_H_ -#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_gl_delegate.h" #include "flutter/testing/test_gl_surface.h" @@ -19,9 +18,7 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, ShellTestPlatformViewGL(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter, - std::shared_ptr - shell_test_external_view_embedder); + CreateVsyncWaiter create_vsync_waiter); virtual ~ShellTestPlatformViewGL() override; @@ -34,9 +31,6 @@ class ShellTestPlatformViewGL : public ShellTestPlatformView, std::shared_ptr vsync_clock_; - std::shared_ptr - shell_test_external_view_embedder_; - // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_test_platform_view_vulkan.cc b/shell/common/shell_test_platform_view_vulkan.cc index 4c21c2dfba36e..e9f41484d9881 100644 --- a/shell/common/shell_test_platform_view_vulkan.cc +++ b/shell/common/shell_test_platform_view_vulkan.cc @@ -11,14 +11,11 @@ ShellTestPlatformViewVulkan::ShellTestPlatformViewVulkan( PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter, - std::shared_ptr - shell_test_external_view_embedder) + CreateVsyncWaiter create_vsync_waiter) : ShellTestPlatformView(delegate, std::move(task_runners)), create_vsync_waiter_(std::move(create_vsync_waiter)), vsync_clock_(vsync_clock), - proc_table_(fml::MakeRefCounted()), - shell_test_external_view_embedder_(shell_test_external_view_embedder) {} + proc_table_(fml::MakeRefCounted()) {} ShellTestPlatformViewVulkan::~ShellTestPlatformViewVulkan() = default; @@ -32,8 +29,7 @@ void ShellTestPlatformViewVulkan::SimulateVSync() { // |PlatformView| std::unique_ptr ShellTestPlatformViewVulkan::CreateRenderingSurface() { - return std::make_unique(proc_table_, - shell_test_external_view_embedder_); + return std::make_unique(proc_table_); } // |PlatformView| @@ -48,12 +44,8 @@ PointerDataDispatcherMaker ShellTestPlatformViewVulkan::GetDispatcherMaker() { // We need to merge this functionality back into //vulkan. // https://github.com/flutter/flutter/issues/51132 ShellTestPlatformViewVulkan::OffScreenSurface::OffScreenSurface( - fml::RefPtr vk, - std::shared_ptr - shell_test_external_view_embedder) - : valid_(false), - vk_(std::move(vk)), - shell_test_external_view_embedder_(shell_test_external_view_embedder) { + fml::RefPtr vk) + : valid_(false), vk_(std::move(vk)) { if (!vk_ || !vk_->HasAcquiredMandatoryProcAddresses()) { FML_DLOG(ERROR) << "Proc table has not acquired mandatory proc addresses."; return; @@ -178,10 +170,5 @@ SkMatrix ShellTestPlatformViewVulkan::OffScreenSurface::GetRootTransformation() return matrix; } -flutter::ExternalViewEmbedder* -ShellTestPlatformViewVulkan::OffScreenSurface::GetExternalViewEmbedder() { - return shell_test_external_view_embedder_.get(); -} - } // namespace testing } // namespace flutter diff --git a/shell/common/shell_test_platform_view_vulkan.h b/shell/common/shell_test_platform_view_vulkan.h index 31757f39a30b0..38bce87fa5414 100644 --- a/shell/common/shell_test_platform_view_vulkan.h +++ b/shell/common/shell_test_platform_view_vulkan.h @@ -5,7 +5,6 @@ #ifndef FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ #define FLUTTER_SHELL_COMMON_SHELL_TEST_PLATFORM_VIEW_VULKAN_H_ -#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/gpu/gpu_surface_vulkan_delegate.h" #include "flutter/vulkan/vulkan_application.h" @@ -19,9 +18,7 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { ShellTestPlatformViewVulkan(PlatformView::Delegate& delegate, TaskRunners task_runners, std::shared_ptr vsync_clock, - CreateVsyncWaiter create_vsync_waiter, - std::shared_ptr - shell_test_external_view_embedder); + CreateVsyncWaiter create_vsync_waiter); ~ShellTestPlatformViewVulkan() override; @@ -30,9 +27,7 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { private: class OffScreenSurface : public flutter::Surface { public: - OffScreenSurface(fml::RefPtr vk, - std::shared_ptr - shell_test_external_view_embedder); + OffScreenSurface(fml::RefPtr vk); ~OffScreenSurface() override; @@ -47,13 +42,9 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { // |Surface| GrContext* GetContext() override; - flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; - private: bool valid_; fml::RefPtr vk_; - std::shared_ptr - shell_test_external_view_embedder_; std::unique_ptr application_; std::unique_ptr logical_device_; sk_sp context_; @@ -70,9 +61,6 @@ class ShellTestPlatformViewVulkan : public ShellTestPlatformView { fml::RefPtr proc_table_; - std::shared_ptr - shell_test_external_view_embedder_; - // |PlatformView| std::unique_ptr CreateRenderingSurface() override; diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 720666ae1bb69..c9c1345541fcc 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -23,7 +23,6 @@ #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/shell_test.h" -#include "flutter/shell/common/shell_test_external_view_embedder.h" #include "flutter/shell/common/shell_test_platform_view.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" @@ -140,7 +139,7 @@ TEST_F(ShellTest, return static_cast>( std::make_unique(task_runners)); }, - ShellTestPlatformView::BackendType::kDefaultBackend, nullptr); + ShellTestPlatformView::BackendType::kDefaultBackend); }, [](Shell& shell) { return std::make_unique(shell, shell.GetTaskRunners()); @@ -467,51 +466,6 @@ TEST_F(ShellTest, FrameRasterizedCallbackIsCalled) { DestroyShell(std::move(shell)); } -TEST_F(ShellTest, - ExternalEmbedderEndFrameIsCalledWhenPostPrerollResultIsResubmit) { - auto settings = CreateSettingsForFixture(); - fml::AutoResetWaitableEvent endFrameLatch; - bool end_frame_called = false; - auto end_frame_callback = [&] { - end_frame_called = true; - endFrameLatch.Signal(); - }; - auto external_view_embedder = std::make_shared( - end_frame_callback, PostPrerollResult::kResubmitFrame); - auto shell = CreateShell(std::move(settings), GetTaskRunnersForFixture(), - false, external_view_embedder); - - // Create the surface needed by rasterizer - PlatformViewNotifyCreated(shell.get()); - - auto configuration = RunConfiguration::InferFromSettings(settings); - configuration.SetEntrypoint("emptyMain"); - - RunEngine(shell.get(), std::move(configuration)); - - LayerTreeBuilder builder = [&](std::shared_ptr root) { - SkPictureRecorder recorder; - SkCanvas* recording_canvas = - recorder.beginRecording(SkRect::MakeXYWH(0, 0, 80, 80)); - recording_canvas->drawRect(SkRect::MakeXYWH(0, 0, 80, 80), - SkPaint(SkColor4f::FromColor(SK_ColorRED))); - auto sk_picture = recorder.finishRecordingAsPicture(); - fml::RefPtr queue = fml::MakeRefCounted( - this->GetCurrentTaskRunner(), fml::TimeDelta::FromSeconds(0)); - auto picture_layer = std::make_shared( - SkPoint::Make(10, 10), - flutter::SkiaGPUObject({sk_picture, queue}), false, false); - root->Add(picture_layer); - }; - - PumpOneFrame(shell.get(), 100, 100, builder); - endFrameLatch.Wait(); - - ASSERT_TRUE(end_frame_called); - - DestroyShell(std::move(shell)); -} - TEST(SettingsTest, FrameTimingSetsAndGetsProperly) { // Ensure that all phases are in kPhases. ASSERT_EQ(sizeof(FrameTiming::kPhases), diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 2c2a91f4836a6..3fbaa7dd8a935 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -248,7 +248,7 @@ } void FlutterPlatformViewsController::CancelFrame() { - composition_order_ = active_composition_order_; + composition_order_.clear(); } bool FlutterPlatformViewsController::HasPendingViewOperations() { @@ -267,9 +267,8 @@ if (raster_thread_merger->IsMerged()) { raster_thread_merger->ExtendLeaseTo(kDefaultMergedLeaseDuration); } else { - // Wait until |EndFrame| to merge the threads. - merge_threads_ = true; CancelFrame(); + raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); return PostPrerollResult::kResubmitFrame; } } @@ -469,21 +468,6 @@ bool FlutterPlatformViewsController::SubmitFrame(GrContext* gr_context, std::shared_ptr ios_context, SkCanvas* background_canvas) { - if (merge_threads_) { - // Threads are about to be merged, we drop everything from this frame - // and possibly resubmit the same layer tree in the next frame. - // Before merging thread, we know the code is not running on the main thread. Assert that - FML_DCHECK(![[NSThread currentThread] isMainThread]); - picture_recorders_.clear(); - composition_order_.clear(); - return true; - } - - // Any UIKit related code has to run on main thread. - // When on a non-main thread, we only allow the rest of the method to run if there is no platform - // view. - FML_DCHECK([[NSThread currentThread] isMainThread] || views_to_dispose_.empty()); - DisposeViews(); // Resolve all pending GPU operations before allocating a new surface. @@ -594,14 +578,6 @@ } } -void FlutterPlatformViewsController::EndFrame( - fml::RefPtr raster_thread_merger) { - if (merge_threads_) { - raster_thread_merger->MergeWithLease(kDefaultMergedLeaseDuration); - merge_threads_ = false; - } -} - std::shared_ptr FlutterPlatformViewsController::GetLayer( GrContext* gr_context, std::shared_ptr ios_context, @@ -666,8 +642,6 @@ return; } - FML_DCHECK([[NSThread currentThread] isMainThread]); - for (int64_t viewId : views_to_dispose_) { UIView* root_view = root_views_[viewId].get(); [root_view removeFromSuperview]; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 6532be8ca14c2..e998640777c96 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -134,8 +134,6 @@ class FlutterPlatformViewsController { void SetFrameSize(SkISize frame_size); - // Indicates that we don't compisite any platform views or overlays during this frame. - // Also reverts the composition_order_ to its original state at the begining of the frame. void CancelFrame(); void PrerollCompositeEmbeddedView(int view_id, @@ -165,11 +163,6 @@ class FlutterPlatformViewsController { std::shared_ptr ios_context, SkCanvas* background_canvas); - // Invoked at the very end of a frame. - // After invoking this method, nothing should happen on the current TaskRunner during the same - // frame. - void EndFrame(fml::RefPtr raster_thread_merger); - void OnMethodCall(FlutterMethodCall* call, FlutterResult& result); private: @@ -280,10 +273,6 @@ class FlutterPlatformViewsController { void ApplyMutators(const MutatorsStack& mutators_stack, UIView* embedded_view); void CompositeWithParams(int view_id, const EmbeddedViewParams& params); - // Default to `false`. - // If `true`, gpu thread and platform thread should be merged during |EndFrame|. - // Always resets to `false` right after the threads are merged. - bool merge_threads_; // Allocates a new FlutterPlatformViewLayer if needed, draws the pixels within the rect from // the picture on the layer's canvas. std::shared_ptr GetLayer(GrContext* gr_context, @@ -293,7 +282,6 @@ class FlutterPlatformViewsController { int64_t view_id, int64_t overlay_id); // Removes overlay views and platform views that aren't needed in the current frame. - // Must run on the platform thread. void RemoveUnusedLayers(); // Appends the overlay views and platform view and sets their z index based on the composition // order. diff --git a/shell/platform/darwin/ios/ios_surface.h b/shell/platform/darwin/ios/ios_surface.h index eae39031d77ce..fd1bc7abb0b48 100644 --- a/shell/platform/darwin/ios/ios_surface.h +++ b/shell/platform/darwin/ios/ios_surface.h @@ -83,9 +83,6 @@ class IOSSurface : public ExternalViewEmbedder { // |ExternalViewEmbedder| void FinishFrame() override; - // |ExternalViewEmbedder| - void EndFrame(fml::RefPtr raster_thread_merger) override; - public: FML_DISALLOW_COPY_AND_ASSIGN(IOSSurface); }; diff --git a/shell/platform/darwin/ios/ios_surface.mm b/shell/platform/darwin/ios/ios_surface.mm index 5f2725273e0dd..cbe2cb4fb8c2c 100644 --- a/shell/platform/darwin/ios/ios_surface.mm +++ b/shell/platform/darwin/ios/ios_surface.mm @@ -90,12 +90,7 @@ bool IsIosEmbeddedViewsPreviewEnabled() { platform_views_controller_->CancelFrame(); // Committing the current transaction as |BeginFrame| will create a nested // CATransaction otherwise. - if ([[NSThread currentThread] isMainThread]) { - // The only time we need to commit the `CATranscation` is when - // there are platform views in the scene, which has to be run on the - // main thread. - [CATransaction commit]; - } + [CATransaction commit]; } // |ExternalViewEmbedder| @@ -103,12 +98,7 @@ bool IsIosEmbeddedViewsPreviewEnabled() { TRACE_EVENT0("flutter", "IOSSurface::BeginFrame"); FML_CHECK(platform_views_controller_ != nullptr); platform_views_controller_->SetFrameSize(frame_size); - if ([[NSThread currentThread] isMainThread]) { - // The only time we need to commit the `CATranscation` is when - // there are platform views in the scene, which has to be run on the - // main thread. - [CATransaction begin]; - } + [CATransaction begin]; } // |ExternalViewEmbedder| @@ -150,22 +140,10 @@ bool IsIosEmbeddedViewsPreviewEnabled() { return submitted; } -// |ExternalViewEmbedder| -void IOSSurface::EndFrame(fml::RefPtr raster_thread_merger) { - TRACE_EVENT0("flutter", "IOSSurface::EndFrame"); - FML_CHECK(platform_views_controller_ != nullptr); - return platform_views_controller_->EndFrame(raster_thread_merger); -} - // |ExternalViewEmbedder| void IOSSurface::FinishFrame() { TRACE_EVENT0("flutter", "IOSSurface::DidSubmitFrame"); - if (![[NSThread currentThread] isMainThread]) { - return; - } - // The only time we need to commit the `CATranscation` is when - // there are platform views in the scene, which has to be run on the - // main thread. [CATransaction commit]; } + } // namespace flutter From 916f014d1cfbfb9c7478c7b05ee36d073af812fc Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 9 Apr 2020 10:28:04 -0400 Subject: [PATCH 327/386] Roll src/third_party/skia f6860405e1bf..8561fc23c927 (7 commits) (#17592) https://skia.googlesource.com/skia.git/+log/f6860405e1bf..8561fc23c927 git log f6860405e1bf..8561fc23c927 --date=short --first-parent --format='%ad %ae %s' 2020-04-08 egdaniel@google.com Update gpu flush semaphore contract. 2020-04-08 senorblanco@chromium.org Dawn: staging manager rewrite. 2020-04-08 fmalita@chromium.org [SVG] Fix spec links 2020-04-08 mtklein@google.com stifle leaks in terminator_CreateDevice() 2020-04-08 mtklein@google.com get skvm/llvm building again 2020-04-08 mtklein@google.com narrow use of fast_unwind_on_malloc=0 2020-04-08 mtklein@google.com shard ASAN bonusconfigs bot Created with: gclient setdep -r src/third_party/skia@8561fc23c927 If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC csmartdalton@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: csmartdalton@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DEPS b/DEPS index 9cec0f4e42655..cd931b02888a9 100644 --- a/DEPS +++ b/DEPS @@ -26,7 +26,7 @@ vars = { 'skia_git': 'https://skia.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': 'f6860405e1bf4603cb393c1818454dc3f893a07a', + 'skia_revision': '8561fc23c927525d370b62b73b431d51ed574935', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index b52ce8df9f4cb..e390b1a7cd837 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 91431c0474883af27c1e7c0e3dee8955 +Signature: cf555339cd3f5572c9e165ed69278b0c UNUSED LICENSES: @@ -1117,6 +1117,7 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.e FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-Lottie.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-CPU-AVX2-x86_64-Release-All-TSAN.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-GCE-GPU-SwiftShader-x86_64-Debug-All-SwiftShader.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-ASAN_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-OpenCL.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-Clang-NUC7i5BNK-GPU-IntelIris640-x86_64-Debug-All-Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/flavor/examples/full.expected/Test-Debian10-GCC-GCE-CPU-AVX2-x86-Debug-All-Docker.json @@ -3108,8 +3109,6 @@ FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRenderTarget.cpp FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRenderTarget.h FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRingBuffer.cpp FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnRingBuffer.h -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStagingManager.cpp -FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStagingManager.h FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStencilAttachment.cpp FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStencilAttachment.h FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnTexture.cpp @@ -5630,7 +5629,11 @@ FILE: ../../../third_party/skia/src/core/SkVerticesPriv.h FILE: ../../../third_party/skia/src/gpu/GrDynamicAtlas.cpp FILE: ../../../third_party/skia/src/gpu/GrDynamicAtlas.h FILE: ../../../third_party/skia/src/gpu/GrEagerVertexAllocator.h +FILE: ../../../third_party/skia/src/gpu/GrStagingBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/GrStagingBuffer.h FILE: ../../../third_party/skia/src/gpu/ccpr/GrAutoMapVertexBuffer.h +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStagingBuffer.cpp +FILE: ../../../third_party/skia/src/gpu/dawn/GrDawnStagingBuffer.h FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.cpp FILE: ../../../third_party/skia/src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h FILE: ../../../third_party/skia/src/gpu/tessellate/GrDrawAtlasPathOp.cpp From 1c6934eaadb319d3a7df5d3848bc0d3e9db4f4b9 Mon Sep 17 00:00:00 2001 From: gaaclarke <30870216+gaaclarke@users.noreply.github.com> Date: Thu, 9 Apr 2020 09:43:38 -0700 Subject: [PATCH 328/386] Enabled hiding the home indicator with SystemChrome.setEnabledSystemUIOverlays (#17574) --- ci/licenses_golden/licenses_flutter | 2 +- .../framework/Source/FlutterPlatformPlugin.mm | 10 +++++ .../framework/Source/FlutterViewController.mm | 37 +++++++++++++++- ...lerTest.m => FlutterViewControllerTest.mm} | 42 ++++++++++++------- .../Source/FlutterViewController_Internal.h | 14 +++++-- .../IosUnitTests.xcodeproj/project.pbxproj | 8 ++-- 6 files changed, 88 insertions(+), 25 deletions(-) rename shell/platform/darwin/ios/framework/Source/{FlutterViewControllerTest.m => FlutterViewControllerTest.mm} (91%) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 5c9e0ac454d9c..453b653dc57a5 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -873,7 +873,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterUmbrell FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterView.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm -FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m +FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index 809c467a4e554..5238be44eaef1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -4,6 +4,7 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h" #include "flutter/fml/logging.h" +#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include #include @@ -161,6 +162,15 @@ - (void)setSystemChromeEnabledSystemUIOverlays:(NSArray*)overlays { // UIViewControllerBasedStatusBarAppearance [UIApplication sharedApplication].statusBarHidden = ![overlays containsObject:@"SystemUiOverlay.top"]; + if ([overlays containsObject:@"SystemUiOverlay.bottom"]) { + [[NSNotificationCenter defaultCenter] + postNotificationName:FlutterViewControllerShowHomeIndicator + object:nil]; + } else { + [[NSNotificationCenter defaultCenter] + postNotificationName:FlutterViewControllerHideHomeIndicator + object:nil]; + } } - (void)restoreSystemChromeSystemUIOverlays { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 2c5f4ebb6d3b0..9c0ff5883fa95 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -27,8 +27,11 @@ static constexpr CGFloat kScrollViewContentSize = 2.0; NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemanticsUpdate"; - NSNotificationName const FlutterViewControllerWillDealloc = @"FlutterViewControllerWillDealloc"; +NSNotificationName const FlutterViewControllerHideHomeIndicator = + @"FlutterViewControllerHideHomeIndicator"; +NSNotificationName const FlutterViewControllerShowHomeIndicator = + @"FlutterViewControllerShowHomeIndicator"; /// Class to coalesce calls for a period of time. /// @@ -91,6 +94,7 @@ - (void)invalidate { // just a warning. @interface FlutterViewController () @property(nonatomic, readwrite, getter=isDisplayingFlutterUI) BOOL displayingFlutterUI; +@property(nonatomic, assign) BOOL isHomeIndicatorHidden; @end // The following conditional compilation defines an API 13 concept on earlier API targets so that @@ -318,6 +322,16 @@ - (void)setupNotificationCenterObservers { selector:@selector(onUserSettingsChanged:) name:UIContentSizeCategoryDidChangeNotification object:nil]; + + [center addObserver:self + selector:@selector(onHideHomeIndicatorNotification:) + name:FlutterViewControllerHideHomeIndicator + object:nil]; + + [center addObserver:self + selector:@selector(onShowHomeIndicatorNotification:) + name:FlutterViewControllerShowHomeIndicator + object:nil]; } - (void)setInitialRoute:(NSString*)route { @@ -1022,6 +1036,27 @@ - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences { } } +- (void)onHideHomeIndicatorNotification:(NSNotification*)notification { + self.isHomeIndicatorHidden = YES; +} + +- (void)onShowHomeIndicatorNotification:(NSNotification*)notification { + self.isHomeIndicatorHidden = NO; +} + +- (void)setIsHomeIndicatorHidden:(BOOL)hideHomeIndicator { + if (hideHomeIndicator != _isHomeIndicatorHidden) { + _isHomeIndicatorHidden = hideHomeIndicator; + if (@available(iOS 11, *)) { + [self setNeedsUpdateOfHomeIndicatorAutoHidden]; + } + } +} + +- (BOOL)prefersHomeIndicatorAutoHidden { + return self.isHomeIndicatorHidden; +} + - (BOOL)shouldAutorotate { return YES; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm similarity index 91% rename from shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m rename to shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index 403e4e3625311..d851616720905 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -6,6 +6,7 @@ #import #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #include "FlutterBinaryMessenger.h" @@ -344,67 +345,67 @@ - (void)testPerformOrientationUpdateDoesNotForceOrientationChange { [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll currentOrientation:UIInterfaceOrientationPortrait didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll currentOrientation:UIInterfaceOrientationPortraitUpsideDown didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll currentOrientation:UIInterfaceOrientationLandscapeLeft didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll currentOrientation:UIInterfaceOrientationLandscapeRight didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown currentOrientation:UIInterfaceOrientationPortrait didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown currentOrientation:UIInterfaceOrientationLandscapeLeft didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown currentOrientation:UIInterfaceOrientationLandscapeRight didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait currentOrientation:UIInterfaceOrientationPortrait didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown currentOrientation:UIInterfaceOrientationPortraitUpsideDown didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape currentOrientation:UIInterfaceOrientationLandscapeLeft didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape currentOrientation:UIInterfaceOrientationLandscapeRight didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft currentOrientation:UIInterfaceOrientationLandscapeLeft didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight currentOrientation:UIInterfaceOrientationLandscapeRight didChangeOrientation:NO - resultingOrientation:0]; + resultingOrientation:static_cast(0)]; } // Perform an orientation update test that fails when the expected outcome @@ -465,8 +466,6 @@ - (void)testWillDeallocNotification { } - (void)testDoesntLoadViewInInit { - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"notification called"]; FlutterDartProject* project = [[FlutterDartProject alloc] init]; FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; [engine createShell:@"" libraryURI:@""]; @@ -476,4 +475,17 @@ - (void)testDoesntLoadViewInInit { XCTAssertFalse([realVC isViewLoaded], @"shouldn't have loaded since it hasn't been shown"); } +- (void)testHideOverlay { + FlutterDartProject* project = [[FlutterDartProject alloc] init]; + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; + [engine createShell:@"" libraryURI:@""]; + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden, @""); + [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator + object:nil]; + XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden, @""); +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index afe5b8166ce4a..98f222b3254ac 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -5,16 +5,22 @@ #ifndef FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ #define FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_ -#include "flutter/flow/embedded_views.h" #include "flutter/fml/memory/weak_ptr.h" -#include "flutter/fml/platform/darwin/scoped_nsobject.h" -#include "flutter/shell/common/shell.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h" + +namespace flutter { +class FlutterPlatformViewsController; +} FLUTTER_EXPORT extern NSNotificationName const FlutterViewControllerWillDealloc; +FLUTTER_EXPORT +extern NSNotificationName const FlutterViewControllerHideHomeIndicator; + +FLUTTER_EXPORT +extern NSNotificationName const FlutterViewControllerShowHomeIndicator; + @interface FlutterViewController () - (fml::WeakPtr)getWeakPtr; diff --git a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj index 533ff80fd389c..1ed8dfcd26acc 100644 --- a/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj +++ b/testing/ios/IosUnitTests/IosUnitTests.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; + 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 0D1CE5D8233430F400E5D880 /* FlutterChannelsTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D1CE5D7233430F400E5D880 /* FlutterChannelsTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 0D4C3FB022DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; 0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; }; @@ -76,7 +76,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterViewControllerTest.m; sourceTree = ""; }; + 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterViewControllerTest.mm; sourceTree = ""; }; 0D1CE5D7233430F400E5D880 /* FlutterChannelsTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterChannelsTest.m; sourceTree = ""; }; 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FlutterPluginAppLifeCycleDelegateTest.m; sourceTree = ""; }; 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FlutterBinaryMessengerRelayTest.mm; sourceTree = ""; }; @@ -179,7 +179,7 @@ 0D52D3B622C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm */, 0D6AB6E722BB40CF00EEE540 /* FlutterEngineTest.mm */, 3D8AF6172384C5420033B95F /* FlutterTextInputPluginTest.m */, - 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.m */, + 0D17A5BF22D78FCD0057279F /* FlutterViewControllerTest.mm */, 0D4C3FAF22DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m */, ); name = Source; @@ -391,7 +391,7 @@ files = ( 0D6AB6EB22BB40E700EEE540 /* FlutterEngineTest.mm in Sources */, 3D8AF6182384C5420033B95F /* FlutterTextInputPluginTest.m in Sources */, - 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.m in Sources */, + 0D17A5C022D78FCD0057279F /* FlutterViewControllerTest.mm in Sources */, 0D1CE5D8233430F400E5D880 /* FlutterChannelsTest.m in Sources */, 0D52D3BD22C566D50011DEBD /* FlutterBinaryMessengerRelayTest.mm in Sources */, 0D4C3FB022DF9F5300A67C70 /* FlutterPluginAppLifeCycleDelegateTest.m in Sources */, From 9c63acd0cc8c7c108a74a0f80fee84dbe12cac71 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 9 Apr 2020 13:41:00 -0400 Subject: [PATCH 329/386] Roll fuchsia/sdk/core/mac-amd64 from dCOo1... to REQ-c... (#17604) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index cd931b02888a9..256fefb1bf7c4 100644 --- a/DEPS +++ b/DEPS @@ -532,7 +532,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'dCOo1xXhC8Ozupm7VR8UF6XvdgcrLRO2ZmNzaZ3Jk3oC' + 'version': 'REQ-c4gnypoLDrJp8vxMd6VeQXzD3xHdgjWRPwShcHcC' } ], 'condition': 'host_os == "mac"', From e47980d1708675873e70538df1d9b2da088d0ad8 Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Thu, 9 Apr 2020 12:28:08 -0700 Subject: [PATCH 330/386] Pull a sysroot on Linux (#17572) This rolls DEPS to pick up the new buildroot sysroot scripts, and uses them to download a sysroot on Linux. The sysroot isn't yet used by default; that will be added in a later PR. Part of https://github.com/flutter/flutter/issues/53176 --- DEPS | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 256fefb1bf7c4..9f38aefa182e5 100644 --- a/DEPS +++ b/DEPS @@ -113,6 +113,9 @@ vars = { # Checkout Windows dependencies only if we are building on Windows. 'download_windows_deps' : 'host_os == "win"', + # Checkout Linux dependencies only when building on Linux. + 'download_linux_deps': 'host_os == "linux"', + # An LLVM backend needs LLVM binaries and headers. To avoid build time # increases we can use prebuilts. We don't want to download this on every # CQ/CI bot nor do we want the average Dart developer to incur that cost. @@ -136,7 +139,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '3a27de1c5bb0f50be7d3efe3e00de4f6068c9f30', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '74eedc7f351b2b8c323468c4487b1b5e9b08f0aa', # Fuchsia compatibility # @@ -622,4 +625,13 @@ hooks = [ 'src/third_party/dart/third_party/7zip.tar.gz.sha1', ], }, + { + 'name': 'linux_sysroot', + 'pattern': '.', + 'condition': 'download_linux_deps', + 'action': [ + 'python', + 'src/build/linux/sysroot_scripts/install-sysroot.py', + '--arch=x64'], + }, ] From 27ec81851edae77545092b0749cf1c507a143eee Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 9 Apr 2020 16:43:20 -0400 Subject: [PATCH 331/386] Roll src/third_party/dart 28c4c6c156e5..114752421dca (17 commits) (#17596) https://dart.googlesource.com/sdk.git/+log/28c4c6c156e5..114752421dca git log 28c4c6c156e5..114752421dca --date=short --first-parent --format='%ad %ae %s' 2020-04-08 srujzs@google.com [dart:html] Remove multitest usage in some js tests 2020-04-08 jcollins@google.com Switch migration to the isolate client. 2020-04-08 jwren@google.com Follow up on https://dart-review.googlesource.com/c/sdk/+/142705 to fix the assertion in the new completions in FieldDeclarations 2020-04-08 rmacnak@google.com [vm, service] Explicitly mention that timeline events are not ordered. 2020-04-08 rnystrom@google.com Migrate language_2/factory to NNBD. 2020-04-08 rnystrom@google.com Migrate language_2/field to NNBD. 2020-04-08 nshahan@google.com [ddc] Run modular test suite in weak mode 2020-04-08 sra@google.com [js_runtime] Streamline String.fromCharCodes 2020-04-08 joshualitt@google.com [dart2js] Always compute platform binaries. 2020-04-08 scheglov@google.com Issue 41399. Check for 'dynamic' and 'Never' when verifying EXPORT_LEGACY_SYMBOL. 2020-04-08 scheglov@google.com Make all named parameters of BestPracticesVerifier required. 2020-04-08 brianwilkerson@google.com Update code_metrics to support running it over a corpus 2020-04-08 scheglov@google.com Support for analysis options in FileResolver. 2020-04-08 scheglov@google.com Remove the special case for getGreatestLowerBound() in inference. 2020-04-08 scheglov@google.com Don't report MISSING_DEFAULT_VALUE_FOR_PARAMETE for external factory constructors. 2020-04-08 kustermann@google.com Revert "Reland "[vm, gc] Parallel scavenge."" 2020-04-08 kustermann@google.com [vm/compiler] Use pc-relative calls to various stubs by moving the stubs to the isolate Created with: gclient setdep -r src/third_party/dart@114752421dca If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/dart-sdk-flutter-engine Please CC dart-vm-team@google.com on the revert to ensure that a human is aware of the problem. To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+/master/autoroll/README.md Bug: None Tbr: dart-vm-team@google.com --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 9f38aefa182e5..06619ad4d2cae 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '28c4c6c156e5c6eb5d3013e9d54ee8a7037e7fa6', + 'dart_revision': '114752421dca6e5adfe83a90bab766e89f586dce', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 9d3474ce8f380..b85851697e659 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 0385a9d418306c4eadb3be682a5f2a37 +Signature: 3b518f8f6b62928f1028b55e86ae00bc UNUSED LICENSES: From 2a8625791a29304ba436d48c2b9c20169f9bd6a3 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Thu, 9 Apr 2020 15:22:22 -0700 Subject: [PATCH 332/386] Eliminate verify_framework presubmit step (#17617) This was originally added in 3c1c3e26d4e96d8f6f7a313b5e609da3c164378d as a means of avoiding running framework tests if they're known to be failing, but given how flaky Cirrus infrastructure has historically been, the number of false positives is too high. --- .cirrus.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index d672e76ec8e93..5dbeef441a142 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -100,9 +100,6 @@ task: mkdir -p $FRAMEWORK_PATH cd $FRAMEWORK_PATH git clone https://github.com/flutter/flutter.git - verify_framework_script: - - echo "Checking that the framework Cirrus test is not currently broken..." - - curl -s https://api.cirrus-ci.com/github/flutter/flutter.json | grep -q passing test_web_script: | cd $FRAMEWORK_PATH/flutter/dev/integration_tests/web ../../../bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web From 9101b63f9872d23627c39f2920ceb8e058059f08 Mon Sep 17 00:00:00 2001 From: Joshua Seaton Date: Thu, 9 Apr 2020 16:09:31 -0700 Subject: [PATCH 333/386] [tools][fuchsia] Reland 'Do not tar debug symbol CIPD uploads' (#17597) Fuchsia is the main consumer of these CIPD packages and - to simplify its infrastructure - it is migrating its SDK partners over to producing a CIPD package containing a flat .build-id directory. This change also updates the CIPD package so that the .build-id directory is placed at the root. Bug: fxbug.dev/41443 --- .../fuchsia/merge_and_upload_debug_symbols.py | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/tools/fuchsia/merge_and_upload_debug_symbols.py b/tools/fuchsia/merge_and_upload_debug_symbols.py index 3bf5d852ca972..b40d91d62a7a5 100755 --- a/tools/fuchsia/merge_and_upload_debug_symbols.py +++ b/tools/fuchsia/merge_and_upload_debug_symbols.py @@ -73,16 +73,25 @@ def ProcessCIPDPackage(upload, cipd_yaml, engine_version, out_dir, target_arch): if tries == num_tries - 1: raise -def CreateTarFile(folder_path, base_dir): - archive_name = os.path.basename(folder_path) - tar_file_path = os.path.join(base_dir, archive_name + '.tar.bz2') - with tarfile.open(tar_file_path, "w:bz2") as archive: - for root, dirs, _ in os.walk(folder_path): - for dir_name in dirs: - dir_path = os.path.join(root, dir_name) - archive.add(dir_path, arcname=dir_name) - return tar_file_path - +# Recursively hardlinks contents from one directory to another, +# skipping over collisions. +def HardlinkContents(dirA, dirB): + for src_dir, _, filenames in os.walk(dirA): + for filename in filenames: + src = os.path.join(src_dir, filename) + dest_dir = os.path.join(dirB, os.path.relpath(src_dir, dirA)) + try: + os.makedirs(dest_dir) + except: + pass + dest = os.path.join(dest_dir, filename) + if os.path.exists(dest): + # The last two path components provide a content address for a .build-id entry. + tokens = os.path.split(dest) + name = os.path.join(tokens[-2], tokens[-1]) + print('%s already exists in destination; skipping linking' % name) + continue + os.link(src, dest) def main(): parser = argparse.ArgumentParser() @@ -114,18 +123,17 @@ def main(): for symbol_dir in symbol_dirs: assert os.path.exists(symbol_dir) and os.path.isdir(symbol_dir) - arch = args.target_arch - out_dir = os.path.join(args.out_dir, - 'flutter-fuchsia-debug-symbols-%s' % arch) + out_dir = args.out_dir + if os.path.exists(out_dir): print 'Directory: %s is not empty, deleting it.' % out_dir shutil.rmtree(out_dir) os.makedirs(out_dir) for symbol_dir in symbol_dirs: - archive_path = CreateTarFile(symbol_dir, out_dir) - print('Created archive: ' + archive_path) + HardlinkContents(symbol_dir, out_dir) + arch = args.target_arch cipd_def = WriteCIPDDefinition(arch, out_dir) ProcessCIPDPackage(args.upload, cipd_def, args.engine_version, out_dir, arch) return 0 From d5d33f2f3c49aad7587b4265ecbf1cc215e85289 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 9 Apr 2020 23:00:02 -0400 Subject: [PATCH 334/386] Roll src/third_party/dart 114752421dca..9f65693f5772 (30 commits) (#17623) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 06619ad4d2cae..7a9aab262d249 100644 --- a/DEPS +++ b/DEPS @@ -34,7 +34,7 @@ vars = { # Dart is: https://github.com/dart-lang/sdk/blob/master/DEPS. # You can use //tools/dart/create_updated_flutter_deps.py to produce # updated revision list of existing dependencies. - 'dart_revision': '114752421dca6e5adfe83a90bab766e89f586dce', + 'dart_revision': '9f65693f5772f9b859a16baa2381462920abbee9', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index b85851697e659..5be3eb40d2658 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 3b518f8f6b62928f1028b55e86ae00bc +Signature: 5378c4c7f1771d4587cd7f93a9f22ad2 UNUSED LICENSES: From 63f40903de0fa9102a3c7db1c47f7a4c86f895b6 Mon Sep 17 00:00:00 2001 From: Shi-Hao Hong Date: Thu, 9 Apr 2020 20:44:37 -0700 Subject: [PATCH 335/386] Revert getSystemGestureExclusionRects and setSystemGestureExclusionRects (#17613) * Revert getSystemGestureExclusionRects and setSystemGestureExclusionRects * Remove references to removed PlatformChannelTest.java file --- shell/platform/android/BUILD.gn | 1 - .../systemchannels/PlatformChannel.java | 98 --------- .../plugin/platform/PlatformPlugin.java | 32 --- .../test/io/flutter/FlutterTestSuite.java | 2 - .../systemchannels/PlatformChannelTest.java | 203 ------------------ 5 files changed, 336 deletions(-) delete mode 100644 shell/platform/android/test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 77d4192cb5001..57992c62961c2 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -437,7 +437,6 @@ action("robolectric_tests") { "test/io/flutter/embedding/engine/dart/DartExecutorTest.java", "test/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistryTest.java", "test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java", - "test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java", "test/io/flutter/external/FlutterLaunchTests.java", "test/io/flutter/plugin/common/StandardMessageCodecTest.java", "test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java", diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java index 3c7d33b306765..f7eff27e60423 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java @@ -5,7 +5,6 @@ package io.flutter.embedding.engine.systemchannels; import android.content.pm.ActivityInfo; -import android.graphics.Rect; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -15,7 +14,6 @@ import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import org.json.JSONArray; import org.json.JSONException; @@ -125,31 +123,6 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result platformMessageHandler.popSystemNavigator(); result.success(null); break; - case "SystemGestures.getSystemGestureExclusionRects": - List exclusionRects = platformMessageHandler.getSystemGestureExclusionRects(); - if (exclusionRects == null) { - String incorrectApiLevel = "Exclusion rects only exist for Android API 29+."; - result.error("error", incorrectApiLevel, null); - break; - } - - ArrayList> encodedExclusionRects = - encodeExclusionRects(exclusionRects); - result.success(encodedExclusionRects); - break; - case "SystemGestures.setSystemGestureExclusionRects": - if (!(arguments instanceof JSONArray)) { - String inputTypeError = - "Input type is incorrect. Ensure that a List> is passed as the input for SystemGestureExclusionRects.setSystemGestureExclusionRects."; - result.error("inputTypeError", inputTypeError, null); - break; - } - - JSONArray inputRects = (JSONArray) arguments; - ArrayList decodedRects = decodeExclusionRects(inputRects); - platformMessageHandler.setSystemGestureExclusionRects(decodedRects); - result.success(null); - break; case "Clipboard.getData": { String contentFormatName = (String) arguments; @@ -295,68 +268,6 @@ private int decodeOrientations(@NonNull JSONArray encodedOrientations) return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; } - /** - * Decodes a JSONArray of rectangle data into an ArrayList. - * - *