From 5353d5dc2854ce76bb12da0efc000039666afec2 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 30 Sep 2020 21:07:02 -0400 Subject: [PATCH 01/31] Roll Fuchsia Mac SDK from aO19K7Ut2... to U2Cd8uL4-... (#21529) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 8a06a7b45d0a3..3c5e1e3f18150 100644 --- a/DEPS +++ b/DEPS @@ -516,7 +516,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'aO19K7Ut2ohAQX1znU6G3kVaX_Z-j3h1j_a7H7xQsy4C' + 'version': 'U2Cd8uL4-2RlaVrEKlJAyesXfZIeI7lpVgaC47laOzsC' } ], 'condition': 'host_os == "mac"', From baecbd4382e93b336f40ec58e521b5c43ceb0cb2 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 30 Sep 2020 23:17:01 -0400 Subject: [PATCH 02/31] Roll Fuchsia Linux SDK from sDtTSnOFx... to GEYgsTBRM... (#21533) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 3c5e1e3f18150..b3b33b4f07243 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'sDtTSnOFx2Wk42TgB2L3U2MZyHq_I7gIEEaHntDqhPMC' + 'version': 'GEYgsTBRMM7gba_hii1JYeBIMKWHNF-nw4W-1PyXjnsC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 5c4af5fa39014..8f477ed71ec4e 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: dd575afe12f43232f0ea0efc7370600f +Signature: 310e1e60e8d88fb6171796cb2499a68c UNUSED LICENSES: From f01936c6d6e19469a0669db5b6ba1cf3e72c4ca3 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 1 Oct 2020 09:07:01 -0400 Subject: [PATCH 03/31] Roll Dart SDK from 0ed6ae6d709e to ddbeaabe8b3d (1 revision) (#21537) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index b3b33b4f07243..3688fbd4f721a 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': '0ed6ae6d709e545ff691910494d45892a8b31e7b', + 'dart_revision': 'ddbeaabe8b3dc6299d9c963d414f820198edf8cd', # 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 e2e5d88f6f5eb..0278bb4a095b0 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 385523a088629e27bbf89457373ff6ab +Signature: f7f2dde13633b0c56c6d8b79c881dace UNUSED LICENSES: From 1c97ac750d5a8867968b03ea2611c531aeb054e8 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 1 Oct 2020 10:12:02 -0400 Subject: [PATCH 04/31] Roll Fuchsia Mac SDK from U2Cd8uL4-... to UKTUc5UVB... (#21538) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index 3688fbd4f721a..f24cbc3d8d4c1 100644 --- a/DEPS +++ b/DEPS @@ -516,7 +516,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'U2Cd8uL4-2RlaVrEKlJAyesXfZIeI7lpVgaC47laOzsC' + 'version': 'UKTUc5UVBDJaJFvP4rO5euHhTXsSx0TDo2rwyCTW2MsC' } ], 'condition': 'host_os == "mac"', From 0c780cafe8fc541a477c914fb790a1167ac49741 Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Thu, 1 Oct 2020 09:07:00 -0700 Subject: [PATCH 05/31] Revert "Roll Dart SDK from 0ed6ae6d709e to ddbeaabe8b3d (1 revision)" (#21540) This reverts commit f01936c6d6e19469a0669db5b6ba1cf3e72c4ca3. --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index f24cbc3d8d4c1..65802bcf6293a 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': 'ddbeaabe8b3dc6299d9c963d414f820198edf8cd', + 'dart_revision': '0ed6ae6d709e545ff691910494d45892a8b31e7b', # 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 0278bb4a095b0..e2e5d88f6f5eb 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: f7f2dde13633b0c56c6d8b79c881dace +Signature: 385523a088629e27bbf89457373ff6ab UNUSED LICENSES: From f0ab5b5ced81ded7a01a211023fb88387b706173 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 1 Oct 2020 12:37:02 -0400 Subject: [PATCH 06/31] Roll Fuchsia Linux SDK from GEYgsTBRM... to EdtRxRaCS... (#21541) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 65802bcf6293a..a52cb91ab274a 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'GEYgsTBRMM7gba_hii1JYeBIMKWHNF-nw4W-1PyXjnsC' + 'version': 'EdtRxRaCSBtWqAj2Ofaisqxa6baoiKQ9th1K-3R3U14C' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 8f477ed71ec4e..b07b7eb3e1317 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 310e1e60e8d88fb6171796cb2499a68c +Signature: 9b48e46f159d6de579f7f185cc23dec4 UNUSED LICENSES: From 995965d36b05cf7e7c0b820b0a96ded820177f15 Mon Sep 17 00:00:00 2001 From: Yilong Li Date: Fri, 2 Oct 2020 02:08:25 +0900 Subject: [PATCH 07/31] fuchsia: Remove display device availability check from Flutter. (#21495) When Flutter engine connects to Scenic, Scenic has already checked the display and graphics device availability before Scenic starts; so it is guaranteed that display devices are available and surface is valid when it is created. Thus this change removes the device watching details from flutter surface on Fuchsia so that it doesn't need to do duplicated checks and hides the device-specific details. --- shell/platform/fuchsia/flutter/surface.cc | 26 +---------------------- shell/platform/fuchsia/flutter/surface.h | 3 --- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/shell/platform/fuchsia/flutter/surface.cc b/shell/platform/fuchsia/flutter/surface.cc index fdcb42a3c7dd7..61f2d25878598 100644 --- a/shell/platform/fuchsia/flutter/surface.cc +++ b/shell/platform/fuchsia/flutter/surface.cc @@ -24,7 +24,7 @@ Surface::~Surface() = default; // |flutter::Surface| bool Surface::IsValid() { - return valid_; + return true; } // |flutter::Surface| @@ -42,30 +42,6 @@ GrDirectContext* Surface::GetContext() { return gr_context_; } -static zx_status_t DriverWatcher(int dirfd, - int event, - const char* fn, - void* cookie) { - if (event == WATCH_EVENT_ADD_FILE && !strcmp(fn, "000")) { - return ZX_ERR_STOP; - } - return ZX_OK; -} - -bool Surface::CanConnectToDisplay() { - constexpr char kGpuDriverClass[] = "/dev/class/gpu"; - fml::UniqueFD fd(open(kGpuDriverClass, O_DIRECTORY | O_RDONLY)); - if (fd.get() < 0) { - FML_DLOG(ERROR) << "Failed to open " << kGpuDriverClass; - return false; - } - - zx_status_t status = fdio_watch_directory( - fd.get(), DriverWatcher, - zx::deadline_after(zx::duration(ZX_SEC(5))).get(), nullptr); - return status == ZX_ERR_STOP; -} - // |flutter::Surface| SkMatrix Surface::GetRootTransformation() const { // This backend does not support delegating to the underlying platform to diff --git a/shell/platform/fuchsia/flutter/surface.h b/shell/platform/fuchsia/flutter/surface.h index 8ebb89de0da24..96222c0f2d326 100644 --- a/shell/platform/fuchsia/flutter/surface.h +++ b/shell/platform/fuchsia/flutter/surface.h @@ -22,7 +22,6 @@ class Surface final : public flutter::Surface { ~Surface() override; private: - const bool valid_ = CanConnectToDisplay(); const std::string debug_label_; flutter::ExternalViewEmbedder* view_embedder_; GrDirectContext* gr_context_; @@ -43,8 +42,6 @@ class Surface final : public flutter::Surface { // |flutter::Surface| flutter::ExternalViewEmbedder* GetExternalViewEmbedder() override; - static bool CanConnectToDisplay(); - FML_DISALLOW_COPY_AND_ASSIGN(Surface); }; From 0522ff22cc78e1722c9776cba8615c7b6810ba66 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 1 Oct 2020 13:32:02 -0400 Subject: [PATCH 08/31] Roll Dart SDK from 0ed6ae6d709e to ddbeaabe8b3d (1 revision) (#21542) --- DEPS | 2 +- ci/licenses_golden/licenses_third_party | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index a52cb91ab274a..f8f0f91cc0ad9 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': '0ed6ae6d709e545ff691910494d45892a8b31e7b', + 'dart_revision': 'ddbeaabe8b3dc6299d9c963d414f820198edf8cd', # 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 e2e5d88f6f5eb..0278bb4a095b0 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: 385523a088629e27bbf89457373ff6ab +Signature: f7f2dde13633b0c56c6d8b79c881dace UNUSED LICENSES: From fc0616f88a00ab2abad7aa60aabbf8639f72af07 Mon Sep 17 00:00:00 2001 From: David Worsham Date: Thu, 1 Oct 2020 12:19:51 -0700 Subject: [PATCH 09/31] fuchsia: Fix test compile (#21543) --- flow/layers/checkerboard_layertree_unittests.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flow/layers/checkerboard_layertree_unittests.cc b/flow/layers/checkerboard_layertree_unittests.cc index 7fbe9847bfa75..337094ae983dd 100644 --- a/flow/layers/checkerboard_layertree_unittests.cc +++ b/flow/layers/checkerboard_layertree_unittests.cc @@ -285,8 +285,6 @@ TEST_F(CheckerBoardLayerTest, ClipRRectSaveLayerCheckBoard) { TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { constexpr float initial_elevation = 20.0f; SkPath layer_path; - const SkRect paint_bounds = SkRect::MakeXYWH(0, 0, 8, 8); - const SkPaint clip_paint; layer_path.addRect(0, 0, 8, 8).close(); auto layer = std::make_shared( SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, @@ -305,6 +303,8 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not use the direct |Paint()| path there. #if !defined(LEGACY_FUCHSIA_EMBEDDER) + const SkRect paint_bounds = SkRect::MakeXYWH(0, 0, 8, 8); + const SkPaint clip_paint; SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); layer_paint.setAntiAlias(true); @@ -329,8 +329,6 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerNotCheckBoard) { TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { constexpr float initial_elevation = 20.0f; SkPath layer_path; - const SkRect paint_bounds = SkRect::MakeXYWH(0, 0, 8, 8); - const SkPaint clip_paint; layer_path.addRect(0, 0, 8, 8).close(); auto layer = std::make_shared( SK_ColorGREEN, SK_ColorBLACK, initial_elevation, layer_path, @@ -349,6 +347,8 @@ TEST_F(CheckerBoardLayerTest, PhysicalSaveLayerCheckBoard) { // The Fuchsia system compositor handles all elevated PhysicalShapeLayers and // their shadows , so we do not use the direct |Paint()| path there. #if !defined(LEGACY_FUCHSIA_EMBEDDER) + const SkRect paint_bounds = SkRect::MakeXYWH(0, 0, 8, 8); + const SkPaint clip_paint; SkPaint layer_paint; layer_paint.setColor(SK_ColorGREEN); layer_paint.setAntiAlias(true); From 2c3fc4aae0db2eceaea401c1d247310411597c53 Mon Sep 17 00:00:00 2001 From: David Worsham Date: Thu, 1 Oct 2020 12:20:10 -0700 Subject: [PATCH 10/31] embedder: Exclude GL code (#21544) --- shell/platform/embedder/embedder.cc | 13 ++++++--- shell/platform/embedder/embedder_engine.cc | 27 +++++++++++++++---- shell/platform/embedder/embedder_engine.h | 15 ++++++++--- .../embedder/platform_view_embedder.cc | 18 +++++++------ .../embedder/platform_view_embedder.h | 17 +++++++----- 5 files changed, 65 insertions(+), 25 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index d09dd0f4893c7..506a5418cff27 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -59,6 +59,10 @@ extern const intptr_t kPlatformStrongDillSize; #include "rapidjson/rapidjson.h" #include "rapidjson/writer.h" +#ifdef SHELL_ENABLE_GL +#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" +#endif + const int32_t kFlutterSemanticsNodeIdBatchEnd = -1; const int32_t kFlutterSemanticsCustomActionIdBatchEnd = -1; @@ -1034,11 +1038,11 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, return std::make_unique(shell); }; +#ifdef SHELL_ENABLE_GL // TODO(chinmaygarde): This is the wrong spot for this. It belongs in the // platform view jump table. flutter::EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback; -#ifdef SHELL_ENABLE_GL if (config->type == kOpenGL) { const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; if (SAFE_ACCESS(open_gl_config, gl_external_texture_frame_callback, @@ -1135,8 +1139,11 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, std::move(settings), // std::move(run_configuration), // on_create_platform_view, // - on_create_rasterizer, // - external_texture_callback // + on_create_rasterizer // +#ifdef SHELL_ENABLE_GL + , + external_texture_callback // +#endif ); // Release the ownership of the embedder engine to the caller. diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index 7d6f2c5d788f1..bb0aa08bb42d5 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -27,16 +27,24 @@ EmbedderEngine::EmbedderEngine( flutter::Settings settings, RunConfiguration run_configuration, Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer, - EmbedderExternalTextureGL::ExternalTextureCallback - external_texture_callback) + Shell::CreateCallback on_create_rasterizer +#ifdef SHELL_ENABLE_GL + , + EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback +#endif + ) : thread_host_(std::move(thread_host)), task_runners_(task_runners), run_configuration_(std::move(run_configuration)), shell_args_(std::make_unique(std::move(settings), on_create_platform_view, - on_create_rasterizer)), - external_texture_callback_(external_texture_callback) {} + on_create_rasterizer)) +#ifdef SHELL_ENABLE_GL + , + external_texture_callback_(external_texture_callback) +#endif +{ +} EmbedderEngine::~EmbedderEngine() = default; @@ -144,28 +152,37 @@ bool EmbedderEngine::SendPlatformMessage( } bool EmbedderEngine::RegisterTexture(int64_t texture) { +#ifdef SHELL_ENABLE_GL if (!IsValid() || !external_texture_callback_) { return false; } shell_->GetPlatformView()->RegisterTexture( std::make_unique(texture, external_texture_callback_)); +#endif + return true; } bool EmbedderEngine::UnregisterTexture(int64_t texture) { +#ifdef SHELL_ENABLE_GL if (!IsValid() || !external_texture_callback_) { return false; } shell_->GetPlatformView()->UnregisterTexture(texture); +#endif + return true; } bool EmbedderEngine::MarkTextureFrameAvailable(int64_t texture) { +#ifdef SHELL_ENABLE_GL if (!IsValid() || !external_texture_callback_) { return false; } shell_->GetPlatformView()->MarkTextureFrameAvailable(texture); +#endif + return true; } diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 151b489bb9465..4f38ba4410cd0 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -12,9 +12,12 @@ #include "flutter/shell/common/shell.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/platform/embedder/embedder.h" -#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" #include "flutter/shell/platform/embedder/embedder_thread_host.h" +#ifdef SHELL_ENABLE_GL +#include "flutter/shell/platform/embedder/embedder_external_texture_gl.h" +#endif + namespace flutter { struct ShellArgs; @@ -28,9 +31,13 @@ class EmbedderEngine { Settings settings, RunConfiguration run_configuration, Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer, + Shell::CreateCallback on_create_rasterizer +#ifdef SHELL_ENABLE_GL + , EmbedderExternalTextureGL::ExternalTextureCallback - external_texture_callback); + external_texture_callback +#endif + ); ~EmbedderEngine(); @@ -90,8 +97,10 @@ class EmbedderEngine { RunConfiguration run_configuration_; std::unique_ptr shell_args_; std::unique_ptr shell_; +#ifdef SHELL_ENABLE_GL const EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback_; +#endif FML_DISALLOW_COPY_AND_ASSIGN(EmbedderEngine); }; diff --git a/shell/platform/embedder/platform_view_embedder.cc b/shell/platform/embedder/platform_view_embedder.cc index 67d0d3557460b..8d06b3ce164a6 100644 --- a/shell/platform/embedder/platform_view_embedder.cc +++ b/shell/platform/embedder/platform_view_embedder.cc @@ -9,28 +9,30 @@ namespace flutter { PlatformViewEmbedder::PlatformViewEmbedder( PlatformView::Delegate& delegate, flutter::TaskRunners task_runners, - EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table, - bool fbo_reset_after_present, + EmbedderSurfaceSoftware::SoftwareDispatchTable software_dispatch_table, PlatformDispatchTable platform_dispatch_table, std::unique_ptr external_view_embedder) : PlatformView(delegate, std::move(task_runners)), - embedder_surface_(std::make_unique( - gl_dispatch_table, - fbo_reset_after_present, + embedder_surface_(std::make_unique( + software_dispatch_table, std::move(external_view_embedder))), platform_dispatch_table_(platform_dispatch_table) {} +#ifdef SHELL_ENABLE_GL PlatformViewEmbedder::PlatformViewEmbedder( PlatformView::Delegate& delegate, flutter::TaskRunners task_runners, - EmbedderSurfaceSoftware::SoftwareDispatchTable software_dispatch_table, + EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table, + bool fbo_reset_after_present, PlatformDispatchTable platform_dispatch_table, std::unique_ptr external_view_embedder) : PlatformView(delegate, std::move(task_runners)), - embedder_surface_(std::make_unique( - software_dispatch_table, + embedder_surface_(std::make_unique( + gl_dispatch_table, + fbo_reset_after_present, std::move(external_view_embedder))), platform_dispatch_table_(platform_dispatch_table) {} +#endif PlatformViewEmbedder::~PlatformViewEmbedder() = default; diff --git a/shell/platform/embedder/platform_view_embedder.h b/shell/platform/embedder/platform_view_embedder.h index 47c03d41a4596..85d3a91290dee 100644 --- a/shell/platform/embedder/platform_view_embedder.h +++ b/shell/platform/embedder/platform_view_embedder.h @@ -11,10 +11,13 @@ #include "flutter/shell/common/platform_view.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder_surface.h" -#include "flutter/shell/platform/embedder/embedder_surface_gl.h" #include "flutter/shell/platform/embedder/embedder_surface_software.h" #include "flutter/shell/platform/embedder/vsync_waiter_embedder.h" +#ifdef SHELL_ENABLE_GL +#include "flutter/shell/platform/embedder/embedder_surface_gl.h" +#endif + namespace flutter { class PlatformViewEmbedder final : public PlatformView { @@ -40,22 +43,24 @@ class PlatformViewEmbedder final : public PlatformView { compute_platform_resolved_locale_callback; }; - // Creates a platform view that sets up an OpenGL rasterizer. + // Create a platform view that sets up a software rasterizer. PlatformViewEmbedder( PlatformView::Delegate& delegate, flutter::TaskRunners task_runners, - EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table, - bool fbo_reset_after_present, + EmbedderSurfaceSoftware::SoftwareDispatchTable software_dispatch_table, PlatformDispatchTable platform_dispatch_table, std::unique_ptr external_view_embedder); - // Create a platform view that sets up a software rasterizer. +#ifdef SHELL_ENABLE_GL + // Creates a platform view that sets up an OpenGL rasterizer. PlatformViewEmbedder( PlatformView::Delegate& delegate, flutter::TaskRunners task_runners, - EmbedderSurfaceSoftware::SoftwareDispatchTable software_dispatch_table, + EmbedderSurfaceGL::GLDispatchTable gl_dispatch_table, + bool fbo_reset_after_present, PlatformDispatchTable platform_dispatch_table, std::unique_ptr external_view_embedder); +#endif ~PlatformViewEmbedder() override; From 9193e2e487774492b3c00a95ee0705d2e9ea6014 Mon Sep 17 00:00:00 2001 From: nturgut Date: Thu, 1 Oct 2020 12:32:43 -0700 Subject: [PATCH 11/31] cloning flutter repo for luci recipes (#21532) * cloning flutter repo for luci recipes * rename the file, script does not clone the repo, but reset it to the right commit * adding the version logging --- tools/configure_framework_commit.sh | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100755 tools/configure_framework_commit.sh diff --git a/tools/configure_framework_commit.sh b/tools/configure_framework_commit.sh new file mode 100755 index 0000000000000..590f946974848 --- /dev/null +++ b/tools/configure_framework_commit.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e +set -x + +if [[ -z $ENGINE_PATH ]] +then + echo "Please set ENGINE_PATH environment variable." + exit 1 +fi + +# Go to the engine git repo to get the date of the latest commit. +cd $ENGINE_PATH/src/flutter + +# Get latest commit's time for the engine repo. +# Use date based on local time otherwise timezones might get mixed. +LATEST_COMMIT_TIME_ENGINE=`git log -1 --date=local --format="%cd"` +echo "Latest commit time on engine found as $LATEST_COMMIT_TIME_ENGINE" + +if [[ -z $FLUTTER_CLONE_REPO_PATH ]] +then + echo "Please set FLUTTER_CLONE_REPO_PATH environment variable." + exit 1 +else + cd $FLUTTER_CLONE_REPO_PATH +fi + +# Get the time of the youngest commit older than engine commit. +# Git log uses commit date not the author date. +# Before makes the comparison considering the timezone as well. +COMMIT_NO=`git log --before="$LATEST_COMMIT_TIME_ENGINE" -n 1 | grep commit | cut -d ' ' -f2` +echo "Using the flutter/flutter commit $COMMIT_NO"; +git reset --hard $COMMIT_NO + +# Print out the flutter version for troubleshooting +$FLUTTER_CLONE_REPO_PATH/bin/flutter --version From f37a0b12e8f84c37c732ce4cbc1837dd15dd7d8b Mon Sep 17 00:00:00 2001 From: Ferhat Date: Thu, 1 Oct 2020 13:51:38 -0700 Subject: [PATCH 12/31] Fix mono quad winding (#21530) --- .../lib/src/engine/html/path/path_windings.dart | 2 +- .../engine/surface/path/path_winding_test.dart | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/web_ui/lib/src/engine/html/path/path_windings.dart b/lib/web_ui/lib/src/engine/html/path/path_windings.dart index 554a5413c8e36..e922e4a53360a 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_windings.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_windings.dart @@ -146,7 +146,7 @@ class PathWinding { _QuadRoots quadRoots = _QuadRoots(); final int n = quadRoots.findRoots( - startY - 2 * y1 + endY, 2 * (y1 - startY), endY - y); + startY - 2 * y1 + endY, 2 * (y1 - startY), startY - y); assert(n <= 1); double xt; if (0 == n) { diff --git a/lib/web_ui/test/engine/surface/path/path_winding_test.dart b/lib/web_ui/test/engine/surface/path/path_winding_test.dart index 32133e648285b..b5bde4c075dbb 100644 --- a/lib/web_ui/test/engine/surface/path/path_winding_test.dart +++ b/lib/web_ui/test/engine/surface/path/path_winding_test.dart @@ -298,6 +298,7 @@ void testMain() { } } }); + test('Concave lines path', () { final SurfacePath path = SurfacePath(); path.moveTo(-0.284071773, -0.0622361786); @@ -424,6 +425,20 @@ void testMain() { } expect(strokedSin.convexityType, SPathConvexityType.kConcave); }); + + /// Regression test for https://github.com/flutter/flutter/issues/66560. + test('Quadratic', () { + final SurfacePath path = SurfacePath(); + path.moveTo(100.0, 0.0); + path.quadraticBezierTo(200.0, 0.0, 200.0, 100.0); + path.quadraticBezierTo(200.0, 200.0, 100.0, 200.0); + path.quadraticBezierTo(0.0, 200.0, 0.0, 100.0); + path.quadraticBezierTo(0.0, 0.0, 100.0, 0.0); + path.close(); + expect(path.contains(Offset(100, 20)), true); + expect(path.contains(Offset(100, 120)), true); + expect(path.contains(Offset(100, -10)), false); + }); }); } From af5717eb1087242d170cb235385633bb94fe87c4 Mon Sep 17 00:00:00 2001 From: Ferhat Date: Thu, 1 Oct 2020 14:00:01 -0700 Subject: [PATCH 13/31] [web] Fix setPreferredOrientations failure on iOS due to NNBD change. (#21531) --- lib/web_ui/lib/src/engine/dom_renderer.dart | 8 +++---- lib/web_ui/test/engine/window_test.dart | 24 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index a4cb42149a007..9e1a3b50ff375 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -536,18 +536,18 @@ flt-glass-pane * { Future setPreferredOrientation(List? orientations) { final html.Screen screen = html.window.screen!; if (!_unsafeIsNull(screen)) { - final html.ScreenOrientation screenOrientation = - screen.orientation!; + final html.ScreenOrientation? screenOrientation = + screen.orientation; if (!_unsafeIsNull(screenOrientation)) { if (orientations!.isEmpty) { - screenOrientation.unlock(); + screenOrientation!.unlock(); return Future.value(true); } else { String? lockType = _deviceOrientationToLockType(orientations.first); if (lockType != null) { final Completer completer = Completer(); try { - screenOrientation.lock(lockType).then((dynamic _) { + screenOrientation!.lock(lockType).then((dynamic _) { completer.complete(true); }).catchError((dynamic error) { // On Chrome desktop an error with 'not supported on this device diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart index 6c212f1248071..15e381440125a 100644 --- a/lib/web_ui/test/engine/window_test.dart +++ b/lib/web_ui/test/engine/window_test.dart @@ -4,6 +4,7 @@ // @dart = 2.6 import 'dart:async'; +import 'dart:js_util' as js_util; import 'dart:html' as html; import 'dart:typed_data'; @@ -247,6 +248,29 @@ void testMain() { expect(responded, isTrue); }); + /// Regression test for https://github.com/flutter/flutter/issues/66128. + test('setPreferredOrientation responds even if browser doesn\'t support api', () async { + final html.Screen screen = html.window.screen; + js_util.setProperty(screen, 'orientation', null); + bool responded = false; + + final Completer completer = Completer(); + final ByteData inputData = JSONMethodCodec().encodeMethodCall(MethodCall( + 'SystemChrome.setPreferredOrientations', + [])); + + window.sendPlatformMessage( + 'flutter/platform', + inputData, + (outputData) { + responded = true; + completer.complete(); + }, + ); + await completer.future; + expect(responded, true); + }); + test('Window implements locale, locales, and locale change notifications', () async { // This will count how many times we notified about locale changes. int localeChangedCount = 0; From 1f183babd39dab6e0bca9d50688bf8d0bbcb6616 Mon Sep 17 00:00:00 2001 From: David Worsham Date: Thu, 1 Oct 2020 16:07:45 -0700 Subject: [PATCH 14/31] fuchsia: Remove Opacity hole-punch (#21527) --- flow/layers/child_scene_layer.cc | 18 +----------------- flow/layers/layer.h | 1 - flow/layers/opacity_layer.cc | 7 +------ flow/layers/opacity_layer_unittests.cc | 5 +++-- 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/flow/layers/child_scene_layer.cc b/flow/layers/child_scene_layer.cc index 2a51590ff785c..9ba5eff3c5021 100644 --- a/flow/layers/child_scene_layer.cc +++ b/flow/layers/child_scene_layer.cc @@ -20,26 +20,10 @@ void ChildSceneLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { context->child_scene_layer_exists_below = true; CheckForChildLayerBelow(context); - - // An alpha "hole punch" is required if the frame behind us is not opaque. - if (!context->is_opaque) { - set_paint_bounds( - SkRect::MakeXYWH(offset_.fX, offset_.fY, size_.fWidth, size_.fHeight)); - } } void ChildSceneLayer::Paint(PaintContext& context) const { - TRACE_EVENT0("flutter", "ChildSceneLayer::Paint"); - FML_DCHECK(needs_painting()); - FML_DCHECK(needs_system_composite()); - - // If we are being rendered into our own frame using the system compositor, - // then it is neccesary to "punch a hole" in the canvas/frame behind us so - // that group opacity looks correct. - SkPaint paint; - paint.setColor(SK_ColorTRANSPARENT); - paint.setBlendMode(SkBlendMode::kSrc); - context.leaf_nodes_canvas->drawRect(paint_bounds(), paint); + FML_NOTREACHED(); } void ChildSceneLayer::UpdateScene(SceneUpdateContext& context) { diff --git a/flow/layers/layer.h b/flow/layers/layer.h index 123e8db3c1bbe..eeffabacbb325 100644 --- a/flow/layers/layer.h +++ b/flow/layers/layer.h @@ -59,7 +59,6 @@ struct PrerollContext { // These allow us to track properties like elevation, opacity, and the // prescence of a platform view during Preroll. bool has_platform_view = false; - bool is_opaque = true; #if defined(LEGACY_FUCHSIA_EMBEDDER) // True if, during the traversal so far, we have seen a child_scene_layer. // Informs whether a layer needs to be system composited. diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index b488c1c946821..131b1d9f253b7 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -14,11 +14,8 @@ OpacityLayer::OpacityLayer(SkAlpha alpha, const SkPoint& offset) void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { TRACE_EVENT0("flutter", "OpacityLayer::Preroll"); + FML_DCHECK(!GetChildContainer()->layers().empty()); // We can't be a leaf. - ContainerLayer* container = GetChildContainer(); - FML_DCHECK(!container->layers().empty()); // OpacityLayer can't be a leaf. - - const bool parent_is_opaque = context->is_opaque; SkMatrix child_matrix = matrix; child_matrix.postTranslate(offset_.fX, offset_.fY); @@ -26,7 +23,6 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { // reverse transformation to the cull rect to properly cull child layers. context->cull_rect = context->cull_rect.makeOffset(-offset_.fX, -offset_.fY); - context->is_opaque = parent_is_opaque && (alpha_ == SK_AlphaOPAQUE); context->mutators_stack.PushTransform( SkMatrix::Translate(offset_.fX, offset_.fY)); context->mutators_stack.PushOpacity(alpha_); @@ -35,7 +31,6 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { ContainerLayer::Preroll(context, child_matrix); context->mutators_stack.Pop(); context->mutators_stack.Pop(); - context->is_opaque = parent_is_opaque; { set_paint_bounds(paint_bounds().makeOffset(offset_.fX, offset_.fY)); diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index 183dcee52d2ca..1ca9ddb2c4041 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -20,8 +20,9 @@ TEST_F(OpacityLayerTest, LeafLayer) { auto layer = std::make_shared(SK_AlphaOPAQUE, SkPoint::Make(0.0f, 0.0f)); - EXPECT_DEATH_IF_SUPPORTED(layer->Preroll(preroll_context(), SkMatrix()), - "\\!container->layers\\(\\)\\.empty\\(\\)"); + EXPECT_DEATH_IF_SUPPORTED( + layer->Preroll(preroll_context(), SkMatrix()), + "\\!GetChildContainer\\(\\)->layers\\(\\)\\.empty\\(\\)"); } TEST_F(OpacityLayerTest, PaintingEmptyLayerDies) { From de423c176423d5c683bd638e8b4b89413241f54d Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Thu, 1 Oct 2020 23:17:02 -0400 Subject: [PATCH 15/31] Roll Fuchsia Mac SDK from UKTUc5UVB... to gZ122oeKO... (#21554) --- DEPS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEPS b/DEPS index f8f0f91cc0ad9..6239867fdcfdb 100644 --- a/DEPS +++ b/DEPS @@ -516,7 +516,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': 'UKTUc5UVBDJaJFvP4rO5euHhTXsSx0TDo2rwyCTW2MsC' + 'version': 'gZ122oeKO8o63jCEfdrVDN8mIa9IIcta8bJSAAuVoHgC' } ], 'condition': 'host_os == "mac"', From 87a10f240f6b64d5978c97de9e66310ac5004914 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 2 Oct 2020 01:57:02 -0400 Subject: [PATCH 16/31] Roll Fuchsia Linux SDK from EdtRxRaCS... to CG6NCkBX9... (#21556) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/DEPS b/DEPS index 6239867fdcfdb..17907a3f27031 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'EdtRxRaCSBtWqAj2Ofaisqxa6baoiKQ9th1K-3R3U14C' + 'version': 'CG6NCkBX9NVzB8GEXPIrAZfAF_rnSSAgvO0dFGp4P0wC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index b07b7eb3e1317..c36b232a57040 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: 9b48e46f159d6de579f7f185cc23dec4 +Signature: dbb5ba7d07e0a2359f59cdfa309e5708 UNUSED LICENSES: @@ -3139,11 +3139,6 @@ FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/string_view.h FILE: ../../../fuchsia/sdk/linux/arch/arm64/sysroot/include/zircon/testonly-syscalls.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/string_view.h FILE: ../../../fuchsia/sdk/linux/arch/x64/sysroot/include/zircon/testonly-syscalls.h -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/internal/_view_provider_impl.dart -FILE: ../../../fuchsia/sdk/linux/dart/fuchsia_modular/lib/src/module/noop_view_provider.dart -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/fuchsia_scenic_flutter/lib/src/child_view_render_box_2.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/component.dart FILE: ../../../fuchsia/sdk/linux/dart/sl4f/lib/src/device.dart From a446aac02ec1a95356176eace40bffe0eeeee44c Mon Sep 17 00:00:00 2001 From: Luigi Rosso Date: Fri, 2 Oct 2020 09:10:11 -0700 Subject: [PATCH 17/31] Fix for issue flutter/#66502. (#21372) --- lib/web_ui/lib/src/engine/canvaskit/path.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/web_ui/lib/src/engine/canvaskit/path.dart b/lib/web_ui/lib/src/engine/canvaskit/path.dart index 723517eb5ce5e..1302736587424 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/path.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/path.dart @@ -257,6 +257,7 @@ class CkPath implements ui.Path { @override void reset() { + _fillType = ui.PathFillType.nonZero; _skPath.reset(); } From c94537782ce67fcec9f8cb7f9373d6b41664fac4 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 2 Oct 2020 13:52:30 -0400 Subject: [PATCH 18/31] Roll Skia from a87c5076a876 to 36366209d412 (55 revisions) (#21562) https://skia.googlesource.com/skia.git/+log/a87c5076a876..36366209d412 2020-10-02 egdaniel@google.com Require stencil bits on wrapped GrBackendRenderTargets to 0, 8, or 16. 2020-10-02 hcm@google.com Update release notes for M87 branch cut, M88 begin 2020-10-02 kjlubick@google.com [canvaskit] Fix computeTightBounds 2020-10-02 egdaniel@google.com Remove unsized gl stencil support. 2020-10-02 robertphillips@google.com Accumulated non-substantive changes 2020-10-02 brianosman@google.com Revert "Convert sksl_frag.sksl to an IRIntrinsicMap" 2020-10-02 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Chromium from 085332bfbb3a to 1693b2839ab9 (532 revisions) 2020-10-02 skia-autoroll@skia-public.iam.gserviceaccount.com Roll SwiftShader from 5e947adaf26e to 375fee944c76 (2 revisions) 2020-10-02 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ANGLE from b09f16ee7aeb to 1fd3e5d89a8f (8 revisions) 2020-10-02 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Dawn from da5828c06b4c to 16ebcf601d7f (1 revision) 2020-10-02 herb@google.com remove SkMakeSpan use ctor parameter deduction 2020-10-01 brianosman@google.com Convert sksl_frag.sksl to an IRIntrinsicMap 2020-10-01 hcm@google.com Update Skia milestone to 88 2020-10-01 brianosman@google.com Support variables in the intrinsic map, clone them into Programs 2020-10-01 johnstiles@google.com Replace set of entrances with boolean. 2020-10-01 johnstiles@google.com Convert Analysis::NodeCount to NodeCountExceeds. 2020-10-01 fmalita@chromium.org [sksg] Fix uninitialized DashEffect attribute 2020-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ANGLE from fa082bb58ba7 to b09f16ee7aeb (16 revisions) 2020-10-01 egdaniel@google.com Fix vulkan input usage flag for VulkanWindowContext. 2020-10-01 michaelludwig@google.com Remove unused field from DeviceCM 2020-10-01 bsalomon@google.com GrRenderTargetContext takes release proc helper. 2020-10-01 brianosman@google.com Remove unused variable from IR generator 2020-10-01 egdaniel@google.com Move bytesPerPixel query off of GrCaps and add bytesPerBlock query. 2020-10-01 brianosman@google.com Revert "moved SkSL FloatLiteral data into IRNode" 2020-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll SwiftShader from b042f4e70879 to 5e947adaf26e (4 revisions) 2020-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Chromium from cc47e0cdb922 to 085332bfbb3a (612 revisions) 2020-10-01 skia-autoroll@skia-public.iam.gserviceaccount.com Roll Dawn from ad421a248adb to da5828c06b4c (2 revisions) 2020-09-30 bsalomon@google.com Get GrBackendRenderTarget's sample count from MtlTexture. 2020-09-30 robertphillips@google.com Have the GrShadowRRectOp make use of the thread-safe view cache 2020-09-30 robertphillips@google.com Have GrMatrixConvolutionEffect use the thread-safe view cache 2020-09-30 johnstiles@google.com Update TextureEffect to avoid extraneous out variable. 2020-09-30 brianosman@google.com Add Variable::fBuiltin, to track Variables owned by pre-includes 2020-09-30 bsalomon@google.com Return GrBackendTexture from SkPromiseImageTexture by value. 2020-09-30 johnstiles@google.com Update MatrixEffect to avoid extraneous out variable. 2020-09-30 ethannicholas@google.com moved SkSL ExternalFunctionCall's data into IRNode 2020-09-30 johnstiles@google.com Simplify generated GLSL code for trivial FPs. 2020-09-30 senorblanco@chromium.org Dawn: remove persistent TextureView from GrDawnTexture. 2020-09-30 smoreland@google.com Avoid heap initialization for skia allocations. 2020-09-30 brianosman@google.com Move VariableReference ref-adjusting into helper functions 2020-09-30 robertphillips@google.com Make GrRectBlurEffect use the thread-safe view cache 2020-09-30 johnstiles@google.com Calculate texture clamping X/Y coordinates in parallel. 2020-09-30 johnstiles@google.com Inline trivial single-argument constructors directly. 2020-09-30 michaelludwig@google.com Fix GrClipStackClip::preApply to report kClippedOut 2020-09-30 robertphillips@google.com Make GrRRectBlurEffect use the thread-safe uniquely-keyed view cache (take 2) 2020-09-30 johnstiles@google.com Add ProgramWriter, a non-const version of ProgramVisitor. 2020-09-30 johnstiles@google.com Add unit test for inlining trivial arguments. 2020-09-30 ethannicholas@google.com moved SkSL Extension data into IRNode 2020-09-30 csmartdalton@google.com Don't cast emscripten pointers when assembling WebGL interface 2020-09-30 johnstiles@google.com Update nanobench SkSL tests. 2020-09-30 ethannicholas@google.com Revert "Revert "moved SkSL ExpressionStatement's data into IRNode"" 2020-09-30 bsalomon@google.com Add sample count field to GrD3DTextureResourceInfo. 2020-09-30 skia-autoroll@skia-public.iam.gserviceaccount.com Roll ANGLE from ec42b1438fd2 to fa082bb58ba7 (11 revisions) 2020-09-30 johnstiles@google.com Avoid creating temporary variables for nested trivial cases. 2020-09-30 johnstiles@google.com Revert "moved SkSL ExpressionStatement's data into IRNode" 2020-09-30 brianosman@google.com Revert "Make GrRRectBlurEffect use the thread-safe uniquely-keyed view cache" 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 brianosman@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/+doc/master/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 17907a3f27031..e4a86a6d3ba4b 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': 'a87c5076a876e63664566bba55e16b6d4d410234', + 'skia_revision': '36366209d4124d99be561545e02b0a165bd829c4', # 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 99f99dc1b2569..0b038b3544777 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: c8b43171afd115a8618c41b931c64abd +Signature: 8da5610b8b391d0a1c5b1b991441e6d2 UNUSED LICENSES: @@ -3922,6 +3922,7 @@ FILE: ../../../third_party/skia/src/core/SkYUVAPixmaps.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendSemaphore.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendSurfaceMutableState.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendSurfaceMutableStateImpl.h +FILE: ../../../third_party/skia/src/gpu/GrBackendUtils.cpp FILE: ../../../third_party/skia/src/gpu/GrBackendUtils.h FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.cpp FILE: ../../../third_party/skia/src/gpu/GrBlockAllocator.h From 88fd4cea430c44466d58e26dadde2632c7222731 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Fri, 2 Oct 2020 11:23:42 -0700 Subject: [PATCH 19/31] Extract the WindowInsetsAnimation.Callback subclass into a separate class that will be lazily loaded (#21548) WindowInsetsAnimation.Callback was introduced in API level 30. This PR moves the text input plugin's WindowInsetsAnimation.Callback subclass into a class that will only be loaded if the embedding has checked for a sufficient API level. See https://github.com/flutter/flutter/issues/66908 --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/android/BUILD.gn | 1 + .../ImeSyncDeferringInsetsCallback.java | 177 ++++++++++++++++++ .../plugin/editing/TextInputPlugin.java | 162 +--------------- .../plugin/editing/TextInputPluginTest.java | 3 +- 5 files changed, 183 insertions(+), 161 deletions(-) create mode 100644 shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8b6299b8900f8..7540c3abd1309 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -795,6 +795,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardM FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StandardMethodCodec.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/StringCodec.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/FlutterTextUtils.java +FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/localization/LocalizationPlugin.java diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index f5714f1fd0fa6..64a5e9b0cced1 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -213,6 +213,7 @@ android_java_sources = [ "io/flutter/plugin/common/StandardMethodCodec.java", "io/flutter/plugin/common/StringCodec.java", "io/flutter/plugin/editing/FlutterTextUtils.java", + "io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java", "io/flutter/plugin/editing/InputConnectionAdaptor.java", "io/flutter/plugin/editing/TextInputPlugin.java", "io/flutter/plugin/localization/LocalizationPlugin.java", diff --git a/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java b/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java new file mode 100644 index 0000000000000..3942574fa3b95 --- /dev/null +++ b/shell/platform/android/io/flutter/plugin/editing/ImeSyncDeferringInsetsCallback.java @@ -0,0 +1,177 @@ +// 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. + +package io.flutter.plugin.editing; + +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.graphics.Insets; +import android.view.View; +import android.view.WindowInsets; +import android.view.WindowInsetsAnimation; +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; +import java.util.List; + +// Loosely based off of +// https://github.com/android/user-interface-samples/blob/master/WindowInsetsAnimation/app/src/main/java/com/google/android/samples/insetsanimation/RootViewDeferringInsetsCallback.kt +// +// When the IME is shown or hidden, it immediately sends an onApplyWindowInsets call +// with the final state of the IME. This initial call disrupts the animation, which +// causes a flicker in the beginning. +// +// To fix this, this class extends WindowInsetsAnimation.Callback and implements +// OnApplyWindowInsetsListener. We capture and defer the initial call to +// onApplyWindowInsets while the animation completes. When the animation +// finishes, we can then release the call by invoking it in the onEnd callback +// +// The WindowInsetsAnimation.Callback extension forwards the new state of the +// IME inset from onProgress() to the framework. We also make use of the +// onStart callback to detect which calls to onApplyWindowInsets would +// interrupt the animation and defer it. +// +// By implementing OnApplyWindowInsetsListener, we are able to capture Android's +// attempts to call the FlutterView's onApplyWindowInsets. When a call to onStart +// occurs, we can mark any non-animation calls to onApplyWindowInsets() that +// occurs between prepare and start as deferred by using this class' wrapper +// implementation to cache the WindowInsets passed in and turn the current call into +// a no-op. When onEnd indicates the end of the animation, the deferred call is +// dispatched again, this time avoiding any flicker since the animation is now +// complete. +@VisibleForTesting +@TargetApi(30) +@RequiresApi(30) +@SuppressLint({"NewApi", "Override"}) +@Keep +class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback + implements View.OnApplyWindowInsetsListener { + private int overlayInsetTypes; + private int deferredInsetTypes; + + private View view; + private WindowInsets lastWindowInsets; + // True when an animation that matches deferredInsetTypes is active. + // + // While this is active, this class will capture the initial window inset + // sent into lastWindowInsets by flagging needsSave to true, and will hold + // onto the intitial inset until the animation is completed, when it will + // re-dispatch the inset change. + private boolean animating = false; + // When an animation begins, android sends a WindowInset with the final + // state of the animation. When needsSave is true, we know to capture this + // initial WindowInset. + private boolean needsSave = false; + + ImeSyncDeferringInsetsCallback( + @NonNull View view, int overlayInsetTypes, int deferredInsetTypes) { + super(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE); + this.overlayInsetTypes = overlayInsetTypes; + this.deferredInsetTypes = deferredInsetTypes; + this.view = view; + } + + // Add this object's event listeners to its view. + void install() { + view.setWindowInsetsAnimationCallback(this); + view.setOnApplyWindowInsetsListener(this); + } + + // Remove this object's event listeners from its view. + void remove() { + view.setWindowInsetsAnimationCallback(null); + view.setOnApplyWindowInsetsListener(null); + } + + @Override + public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { + this.view = view; + if (needsSave) { + // Store the view and insets for us in onEnd() below. This captured inset + // is not part of the animation and instead, represents the final state + // of the inset after the animation is completed. Thus, we defer the processing + // of this WindowInset until the animation completes. + lastWindowInsets = windowInsets; + needsSave = false; + } + if (animating) { + // While animation is running, we consume the insets to prevent disrupting + // the animation, which skips this implementation and calls the view's + // onApplyWindowInsets directly to avoid being consumed here. + return WindowInsets.CONSUMED; + } + + // If no animation is happening, pass the insets on to the view's own + // inset handling. + return view.onApplyWindowInsets(windowInsets); + } + + @Override + public void onPrepare(WindowInsetsAnimation animation) { + if ((animation.getTypeMask() & deferredInsetTypes) != 0) { + animating = true; + needsSave = true; + } + } + + @Override + public WindowInsets onProgress( + WindowInsets insets, List runningAnimations) { + if (!animating || needsSave) { + return insets; + } + boolean matching = false; + for (WindowInsetsAnimation animation : runningAnimations) { + if ((animation.getTypeMask() & deferredInsetTypes) != 0) { + matching = true; + continue; + } + } + if (!matching) { + return insets; + } + WindowInsets.Builder builder = new WindowInsets.Builder(lastWindowInsets); + // Overlay the ime-only insets with the full insets. + // + // The IME insets passed in by onProgress assumes that the entire animation + // occurs above any present navigation and status bars. This causes the + // IME inset to be too large for the animation. To remedy this, we merge the + // IME inset with other insets present via a subtract + reLu, which causes the + // IME inset to be overlaid with any bars present. + Insets newImeInsets = + Insets.of( + 0, + 0, + 0, + Math.max( + insets.getInsets(deferredInsetTypes).bottom + - insets.getInsets(overlayInsetTypes).bottom, + 0)); + builder.setInsets(deferredInsetTypes, newImeInsets); + // Directly call onApplyWindowInsets of the view as we do not want to pass through + // the onApplyWindowInsets defined in this class, which would consume the insets + // as if they were a non-animation inset change and cache it for re-dispatch in + // onEnd instead. + view.onApplyWindowInsets(builder.build()); + return insets; + } + + @Override + public void onEnd(WindowInsetsAnimation animation) { + if (animating && (animation.getTypeMask() & deferredInsetTypes) != 0) { + // If we deferred the IME insets and an IME animation has finished, we need to reset + // the flags + animating = false; + + // And finally dispatch the deferred insets to the view now. + // Ideally we would just call view.requestApplyInsets() and let the normal dispatch + // cycle happen, but this happens too late resulting in a visual flicker. + // Instead we manually dispatch the most recent WindowInsets to the view. + if (lastWindowInsets != null && view != null) { + view.dispatchApplyWindowInsets(lastWindowInsets); + } + } + } +} diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 3090c6fba81fa..2dc272a7d74be 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -5,9 +5,7 @@ package io.flutter.plugin.editing; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Insets; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; @@ -19,7 +17,6 @@ import android.view.View; import android.view.ViewStructure; import android.view.WindowInsets; -import android.view.WindowInsetsAnimation; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; @@ -28,15 +25,12 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; -import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import io.flutter.embedding.engine.systemchannels.TextInputChannel; import io.flutter.plugin.platform.PlatformViewsController; import java.util.HashMap; -import java.util.List; /** Android implementation of the text input plugin. */ public class TextInputPlugin { @@ -91,8 +85,7 @@ public TextInputPlugin( mask, // Overlay, insets that should be merged with the deferred insets WindowInsets.Type.ime() // Deferred, insets that will animate ); - mView.setWindowInsetsAnimationCallback(imeSyncCallback); - mView.setOnApplyWindowInsetsListener(imeSyncCallback); + imeSyncCallback.install(); } this.textInputChannel = textInputChannel; @@ -164,154 +157,6 @@ public void sendAppPrivateCommand(String action, Bundle data) { restartAlwaysRequired = isRestartAlwaysRequired(); } - // Loosely based off of - // https://github.com/android/user-interface-samples/blob/master/WindowInsetsAnimation/app/src/main/java/com/google/android/samples/insetsanimation/RootViewDeferringInsetsCallback.kt - // - // When the IME is shown or hidden, it immediately sends an onApplyWindowInsets call - // with the final state of the IME. This initial call disrupts the animation, which - // causes a flicker in the beginning. - // - // To fix this, this class extends WindowInsetsAnimation.Callback and implements - // OnApplyWindowInsetsListener. We capture and defer the initial call to - // onApplyWindowInsets while the animation completes. When the animation - // finishes, we can then release the call by invoking it in the onEnd callback - // - // The WindowInsetsAnimation.Callback extension forwards the new state of the - // IME inset from onProgress() to the framework. We also make use of the - // onStart callback to detect which calls to onApplyWindowInsets would - // interrupt the animation and defer it. - // - // By implementing OnApplyWindowInsetsListener, we are able to capture Android's - // attempts to call the FlutterView's onApplyWindowInsets. When a call to onStart - // occurs, we can mark any non-animation calls to onApplyWindowInsets() that - // occurs between prepare and start as deferred by using this class' wrapper - // implementation to cache the WindowInsets passed in and turn the current call into - // a no-op. When onEnd indicates the end of the animation, the deferred call is - // dispatched again, this time avoiding any flicker since the animation is now - // complete. - @VisibleForTesting - @TargetApi(30) - @RequiresApi(30) - @SuppressLint({"NewApi", "Override"}) - @Keep - class ImeSyncDeferringInsetsCallback extends WindowInsetsAnimation.Callback - implements View.OnApplyWindowInsetsListener { - private int overlayInsetTypes; - private int deferredInsetTypes; - - private View view; - private WindowInsets lastWindowInsets; - // True when an animation that matches deferredInsetTypes is active. - // - // While this is active, this class will capture the initial window inset - // sent into lastWindowInsets by flagging needsSave to true, and will hold - // onto the intitial inset until the animation is completed, when it will - // re-dispatch the inset change. - private boolean animating = false; - // When an animation begins, android sends a WindowInset with the final - // state of the animation. When needsSave is true, we know to capture this - // initial WindowInset. - private boolean needsSave = false; - - ImeSyncDeferringInsetsCallback( - @NonNull View view, int overlayInsetTypes, int deferredInsetTypes) { - super(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE); - this.overlayInsetTypes = overlayInsetTypes; - this.deferredInsetTypes = deferredInsetTypes; - this.view = view; - } - - @Override - public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { - this.view = view; - if (needsSave) { - // Store the view and insets for us in onEnd() below. This captured inset - // is not part of the animation and instead, represents the final state - // of the inset after the animation is completed. Thus, we defer the processing - // of this WindowInset until the animation completes. - lastWindowInsets = windowInsets; - needsSave = false; - } - if (animating) { - // While animation is running, we consume the insets to prevent disrupting - // the animation, which skips this implementation and calls the view's - // onApplyWindowInsets directly to avoid being consumed here. - return WindowInsets.CONSUMED; - } - - // If no animation is happening, pass the insets on to the view's own - // inset handling. - return view.onApplyWindowInsets(windowInsets); - } - - @Override - public void onPrepare(WindowInsetsAnimation animation) { - if ((animation.getTypeMask() & deferredInsetTypes) != 0) { - animating = true; - needsSave = true; - } - } - - @Override - public WindowInsets onProgress( - WindowInsets insets, List runningAnimations) { - if (!animating || needsSave) { - return insets; - } - boolean matching = false; - for (WindowInsetsAnimation animation : runningAnimations) { - if ((animation.getTypeMask() & deferredInsetTypes) != 0) { - matching = true; - continue; - } - } - if (!matching) { - return insets; - } - WindowInsets.Builder builder = new WindowInsets.Builder(lastWindowInsets); - // Overlay the ime-only insets with the full insets. - // - // The IME insets passed in by onProgress assumes that the entire animation - // occurs above any present navigation and status bars. This causes the - // IME inset to be too large for the animation. To remedy this, we merge the - // IME inset with other insets present via a subtract + reLu, which causes the - // IME inset to be overlaid with any bars present. - Insets newImeInsets = - Insets.of( - 0, - 0, - 0, - Math.max( - insets.getInsets(deferredInsetTypes).bottom - - insets.getInsets(overlayInsetTypes).bottom, - 0)); - builder.setInsets(deferredInsetTypes, newImeInsets); - // Directly call onApplyWindowInsets of the view as we do not want to pass through - // the onApplyWindowInsets defined in this class, which would consume the insets - // as if they were a non-animation inset change and cache it for re-dispatch in - // onEnd instead. - view.onApplyWindowInsets(builder.build()); - return insets; - } - - @Override - public void onEnd(WindowInsetsAnimation animation) { - if (animating && (animation.getTypeMask() & deferredInsetTypes) != 0) { - // If we deferred the IME insets and an IME animation has finished, we need to reset - // the flags - animating = false; - - // And finally dispatch the deferred insets to the view now. - // Ideally we would just call view.requestApplyInsets() and let the normal dispatch - // cycle happen, but this happens too late resulting in a visual flicker. - // Instead we manually dispatch the most recent WindowInsets to the view. - if (lastWindowInsets != null && view != null) { - view.dispatchApplyWindowInsets(lastWindowInsets); - } - } - } - } - @NonNull public InputMethodManager getInputMethodManager() { return mImm; @@ -364,9 +209,8 @@ public void unlockPlatformViewInputConnection() { public void destroy() { platformViewsController.detachTextInputPlugin(); textInputChannel.setTextInputMethodHandler(null); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - mView.setWindowInsetsAnimationCallback(null); - mView.setOnApplyWindowInsetsListener(null); + if (imeSyncCallback != null) { + imeSyncCallback.remove(); } } 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 b038eb56000f3..7cec8faafb6fd 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -652,8 +652,7 @@ public void ime_windowInsetsSync() { TextInputChannel textInputChannel = new TextInputChannel(mock(DartExecutor.class)); TextInputPlugin textInputPlugin = new TextInputPlugin(testView, textInputChannel, mock(PlatformViewsController.class)); - TextInputPlugin.ImeSyncDeferringInsetsCallback imeSyncCallback = - textInputPlugin.getImeSyncCallback(); + ImeSyncDeferringInsetsCallback imeSyncCallback = textInputPlugin.getImeSyncCallback(); FlutterEngine flutterEngine = spy(new FlutterEngine(RuntimeEnvironment.application, mockFlutterLoader, mockFlutterJni)); FlutterRenderer flutterRenderer = spy(new FlutterRenderer(mockFlutterJni)); From 43828e86fa2ee6cb50d696cbacf18201d86e189b Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Fri, 2 Oct 2020 12:17:02 -0700 Subject: [PATCH 20/31] Workaround for an Android emulator EGL bug that can cause inaccurate GL version strings (#21258) --- shell/gpu/gpu_surface_gl_delegate.h | 2 +- shell/platform/android/android_context_gl.cc | 8 ++++ shell/platform/android/android_context_gl.h | 7 ++++ shell/platform/android/android_surface_gl.cc | 40 ++++++++++++++++++++ shell/platform/android/android_surface_gl.h | 3 ++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/shell/gpu/gpu_surface_gl_delegate.h b/shell/gpu/gpu_surface_gl_delegate.h index 8f2c3df04006f..e35be2c6232bb 100644 --- a/shell/gpu/gpu_surface_gl_delegate.h +++ b/shell/gpu/gpu_surface_gl_delegate.h @@ -57,7 +57,7 @@ class GPUSurfaceGLDelegate : public GPUSurfaceDelegate { // flushed. virtual SkMatrix GLContextSurfaceTransformation() const; - sk_sp GetGLInterface() const; + virtual sk_sp GetGLInterface() const; // TODO(chinmaygarde): The presence of this method is to work around the fact // that not all platforms can accept a custom GL proc table. Migrate all diff --git a/shell/platform/android/android_context_gl.cc b/shell/platform/android/android_context_gl.cc index 91e2628e1fc1b..1a17a9a8f0444 100644 --- a/shell/platform/android/android_context_gl.cc +++ b/shell/platform/android/android_context_gl.cc @@ -248,4 +248,12 @@ bool AndroidContextGL::ClearCurrent() { return true; } +EGLContext AndroidContextGL::CreateNewContext() const { + bool success; + EGLContext context; + std::tie(success, context) = + CreateContext(environment_->Display(), config_, EGL_NO_CONTEXT); + return success ? context : EGL_NO_CONTEXT; +} + } // namespace flutter diff --git a/shell/platform/android/android_context_gl.h b/shell/platform/android/android_context_gl.h index dc9fc3212303e..a0e4f99e81489 100644 --- a/shell/platform/android/android_context_gl.h +++ b/shell/platform/android/android_context_gl.h @@ -110,6 +110,13 @@ class AndroidContextGL : public AndroidContext { /// bool ClearCurrent(); + //---------------------------------------------------------------------------- + /// @brief Create a new EGLContext using the same EGLConfig. + /// + /// @return The EGLContext. + /// + EGLContext CreateNewContext() const; + private: fml::RefPtr environment_; EGLConfig config_; diff --git a/shell/platform/android/android_surface_gl.cc b/shell/platform/android/android_surface_gl.cc index 642fd4f0c65cb..c9a9a130c59c4 100644 --- a/shell/platform/android/android_surface_gl.cc +++ b/shell/platform/android/android_surface_gl.cc @@ -4,6 +4,7 @@ #include "flutter/shell/platform/android/android_surface_gl.h" +#include #include #include "flutter/fml/logging.h" @@ -12,6 +13,12 @@ namespace flutter { +namespace { +// GL renderer string prefix used by the Android emulator GLES implementation. +constexpr char kEmulatorRendererPrefix[] = + "Android Emulator OpenGL ES Translator"; +} // anonymous namespace + AndroidSurfaceGL::AndroidSurfaceGL( std::shared_ptr android_context, std::shared_ptr jni_facade, @@ -133,4 +140,37 @@ ExternalViewEmbedder* AndroidSurfaceGL::GetExternalViewEmbedder() { return external_view_embedder_.get(); } +// |GPUSurfaceGLDelegate| +sk_sp AndroidSurfaceGL::GetGLInterface() const { + // This is a workaround for a bug in the Android emulator EGL/GLES + // implementation. Some versions of the emulator will not update the + // GL version string when the process switches to a new EGL context + // unless the EGL context is being made current for the first time. + // The inaccurate version string will be rejected by Skia when it + // tries to build the GrGLInterface. Flutter can work around this + // by creating a new context, making it current to force an update + // of the version, and then reverting to the previous context. + const char* gl_renderer = + reinterpret_cast(glGetString(GL_RENDERER)); + if (gl_renderer && strncmp(gl_renderer, kEmulatorRendererPrefix, + strlen(kEmulatorRendererPrefix)) == 0) { + EGLContext new_context = android_context_->CreateNewContext(); + if (new_context != EGL_NO_CONTEXT) { + EGLContext old_context = eglGetCurrentContext(); + EGLDisplay display = eglGetCurrentDisplay(); + EGLSurface draw_surface = eglGetCurrentSurface(EGL_DRAW); + EGLSurface read_surface = eglGetCurrentSurface(EGL_READ); + EGLBoolean result = + eglMakeCurrent(display, draw_surface, read_surface, new_context); + FML_DCHECK(result == EGL_TRUE); + result = eglMakeCurrent(display, draw_surface, read_surface, old_context); + FML_DCHECK(result == EGL_TRUE); + result = eglDestroyContext(display, new_context); + FML_DCHECK(result == EGL_TRUE); + } + } + + return GPUSurfaceGLDelegate::GetGLInterface(); +} + } // namespace flutter diff --git a/shell/platform/android/android_surface_gl.h b/shell/platform/android/android_surface_gl.h index 8af382a521e27..771fbfea41a80 100644 --- a/shell/platform/android/android_surface_gl.h +++ b/shell/platform/android/android_surface_gl.h @@ -65,6 +65,9 @@ class AndroidSurfaceGL final : public GPUSurfaceGLDelegate, // |GPUSurfaceGLDelegate| ExternalViewEmbedder* GetExternalViewEmbedder() override; + // |GPUSurfaceGLDelegate| + sk_sp GetGLInterface() const override; + private: const std::unique_ptr external_view_embedder_; const std::shared_ptr android_context_; From 2ce19cec46e9a7b6c8e2b46f3cb73c172c2f172a Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 2 Oct 2020 15:22:02 -0400 Subject: [PATCH 21/31] Roll Skia from 36366209d412 to 8cc5f19f439d (1 revision) (#21565) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index e4a86a6d3ba4b..d70fa5b7ee339 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': '36366209d4124d99be561545e02b0a165bd829c4', + 'skia_revision': '8cc5f19f439d31e832b137664b75b11607096eb3', # 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 0b038b3544777..4419b99ea9999 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 8da5610b8b391d0a1c5b1b991441e6d2 +Signature: ab0c5248141d05498ef0e32b2ef80454 UNUSED LICENSES: From 4f2ed6092b462b3a992167dc90075ddc7e2a9e23 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 2 Oct 2020 15:27:01 -0400 Subject: [PATCH 22/31] Roll Fuchsia Linux SDK from CG6NCkBX9... to 8Evelbjqf... (#21566) --- DEPS | 2 +- ci/licenses_golden/licenses_fuchsia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index d70fa5b7ee339..73974973df9d5 100644 --- a/DEPS +++ b/DEPS @@ -536,7 +536,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'CG6NCkBX9NVzB8GEXPIrAZfAF_rnSSAgvO0dFGp4P0wC' + 'version': '8EvelbjqfxB3wn8AZ87H43bfUHvYKPxYeeticZzeTZoC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index c36b232a57040..8c500409d30ee 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: dbb5ba7d07e0a2359f59cdfa309e5708 +Signature: dadcf60ce8647f0330c020bc282418a0 UNUSED LICENSES: From 2d42c165cfbc8ddc1b2392a45dfc0970d31b79ec Mon Sep 17 00:00:00 2001 From: nturgut Date: Fri, 2 Oct 2020 12:41:39 -0700 Subject: [PATCH 23/31] Writing the commit no to a text file (#21546) * setting an env variable using commit no * change variable name * try to use ref no as string * use echo for output * remove all the other stdout * echo json instead of only the commit no * use the file method * list the directory contents * remove pwd. print flutter version verbose --- tools/configure_framework_commit.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/configure_framework_commit.sh b/tools/configure_framework_commit.sh index 590f946974848..2736dce390472 100755 --- a/tools/configure_framework_commit.sh +++ b/tools/configure_framework_commit.sh @@ -31,5 +31,8 @@ COMMIT_NO=`git log --before="$LATEST_COMMIT_TIME_ENGINE" -n 1 | grep commit | cu echo "Using the flutter/flutter commit $COMMIT_NO"; git reset --hard $COMMIT_NO +# Write the commit number to a file. This file will be read by the LUCI recipe. +echo "$COMMIT_NO" >> flutter_ref.txt + # Print out the flutter version for troubleshooting -$FLUTTER_CLONE_REPO_PATH/bin/flutter --version +$FLUTTER_CLONE_REPO_PATH/bin/flutter --version -v From 89a46af119a4f9b989a6af98da2b64af810e8cd5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Fri, 2 Oct 2020 12:57:01 -0700 Subject: [PATCH 24/31] Implement Image.clone for CanvasKit (#21555) --- .../src/engine/canvaskit/canvaskit_api.dart | 4 ++ .../lib/src/engine/canvaskit/image.dart | 41 +++++++++---- .../engine/canvaskit/skia_object_cache.dart | 58 ++++++++++++++++--- lib/web_ui/test/canvaskit/image_test.dart | 42 ++++++++++++++ .../canvaskit/skia_objects_cache_test.dart | 43 +++++++++++++- 5 files changed, 168 insertions(+), 20 deletions(-) diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index bca5a9b995472..1252cba16e436 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -683,6 +683,8 @@ class SkAnimatedImage { /// /// This object is no longer usable after calling this method. external void delete(); + external bool isAliasOf(SkAnimatedImage other); + external bool isDeleted(); } @JS() @@ -698,6 +700,8 @@ class SkImage { ); external Uint8List readPixels(SkImageInfo imageInfo, int srcX, int srcY); external SkData encodeToData(); + external bool isAliasOf(SkImage other); + external bool isDeleted(); } @JS() diff --git a/lib/web_ui/lib/src/engine/canvaskit/image.dart b/lib/web_ui/lib/src/engine/canvaskit/image.dart index badaa8e6042e4..192e301c1bdcd 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image.dart @@ -43,8 +43,15 @@ class CkAnimatedImage implements ui.Image { // being garbage-collected, or by an explicit call to [delete]. late final SkiaObjectBox box; - CkAnimatedImage(this._skAnimatedImage) { - box = SkiaObjectBox(this, _skAnimatedImage as SkDeletable); + CkAnimatedImage(SkAnimatedImage skAnimatedImage) : this._(skAnimatedImage, null); + + CkAnimatedImage._(this._skAnimatedImage, SkiaObjectBox? boxToClone) { + if (boxToClone != null) { + assert(boxToClone.skObject == _skAnimatedImage); + box = boxToClone.clone(this); + } else { + box = SkiaObjectBox(this, _skAnimatedImage as SkDeletable); + } } @override @@ -53,13 +60,17 @@ class CkAnimatedImage implements ui.Image { } @override - ui.Image clone() => this; + ui.Image clone() => CkAnimatedImage._(_skAnimatedImage, box); @override - bool isCloneOf(ui.Image other) => other == this; + bool isCloneOf(ui.Image other) { + return other is CkAnimatedImage + && other._skAnimatedImage.isAliasOf(_skAnimatedImage); + } @override - List? debugGetOpenHandleStackTraces() => null; + List? debugGetOpenHandleStackTraces() => box.debugGetStackTraces(); + int get frameCount => _skAnimatedImage.getFrameCount(); /// Decodes the next frame and returns the frame duration. @@ -116,8 +127,15 @@ class CkImage implements ui.Image { // being garbage-collected, or by an explicit call to [delete]. late final SkiaObjectBox box; - CkImage(this.skImage) { - box = SkiaObjectBox(this, skImage as SkDeletable); + CkImage(SkImage skImage) : this._(skImage, null); + + CkImage._(this.skImage, SkiaObjectBox? boxToClone) { + if (boxToClone != null) { + assert(boxToClone.skObject == skImage); + box = boxToClone.clone(this); + } else { + box = SkiaObjectBox(this, skImage as SkDeletable); + } } @override @@ -126,13 +144,16 @@ class CkImage implements ui.Image { } @override - ui.Image clone() => this; + ui.Image clone() => CkImage._(skImage, box); @override - bool isCloneOf(ui.Image other) => other == this; + bool isCloneOf(ui.Image other) { + return other is CkImage + && other.skImage.isAliasOf(skImage); + } @override - List? debugGetOpenHandleStackTraces() => null; + List? debugGetOpenHandleStackTraces() => box.debugGetStackTraces(); @override int get width => skImage.width(); diff --git a/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart b/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart index 36cdf7e7c5176..eaca5c43ca4b9 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/skia_object_cache.dart @@ -239,23 +239,46 @@ abstract class OneShotSkiaObject extends SkiaObject { } } -/// Manages the lifecycle of a Skia object owned by a wrapper object. +/// Uses reference counting to manage the lifecycle of a Skia object owned by a +/// wrapper object. /// -/// When the wrapper is garbage collected, deletes the corresponding -/// [skObject] (only in browsers that support weak references). +/// When the wrapper is garbage collected, decrements the refcount (only in +/// browsers that support weak references). /// -/// The [delete] method can be used to eagerly delete the [skObject] -/// before the wrapper is garbage collected. +/// The [delete] method can be used to eagerly decrement the refcount before the +/// wrapper is garbage collected. /// /// The [delete] method may be called any number of times. The box /// will only delete the object once. class SkiaObjectBox { - SkiaObjectBox(Object wrapper, this.skObject) { + SkiaObjectBox(Object wrapper, SkDeletable skObject) + : this._(wrapper, skObject, {}); + + SkiaObjectBox._(Object wrapper, this.skObject, this._refs) { + if (assertionsEnabled) { + _debugStackTrace = StackTrace.current; + } + _refs.add(this); if (browserSupportsFinalizationRegistry) { boxRegistry.register(wrapper, this); } } + /// Reference handles to the same underlying [skObject]. + final Set _refs; + + late final StackTrace? _debugStackTrace; + /// If asserts are enabled, the [StackTrace]s representing when a reference + /// was created. + List? debugGetStackTraces() { + if (assertionsEnabled) { + return _refs + .map((SkiaObjectBox box) => box._debugStackTrace!) + .toList(); + } + return null; + } + /// The Skia object whose lifecycle is being managed. final SkDeletable skObject; @@ -269,16 +292,33 @@ class SkiaObjectBox { box.delete(); })); - /// Deletes the [skObject]. + /// Returns a clone of this object, which increases its reference count. + /// + /// Clones must be [dispose]d when finished. + SkiaObjectBox clone(Object wrapper) { + assert(!_isDeleted, 'Cannot clone from a deleted handle.'); + assert(_refs.isNotEmpty); + return SkiaObjectBox._(wrapper, skObject, _refs); + } + + /// Decrements the reference count for the [skObject]. /// /// Does nothing if the object has already been deleted. + /// + /// If this causes the reference count to drop to zero, deletes the + /// [skObject]. void delete() { if (_isDeleted) { + assert(!_refs.contains(this)); return; } + final bool removed = _refs.remove(this); + assert(removed); _isDeleted = true; - _skObjectDeleteQueue.add(skObject); - _skObjectCollector ??= _scheduleSkObjectCollection(); + if (_refs.isEmpty) { + _skObjectDeleteQueue.add(skObject); + _skObjectCollector ??= _scheduleSkObjectCollection(); + } } } diff --git a/lib/web_ui/test/canvaskit/image_test.dart b/lib/web_ui/test/canvaskit/image_test.dart index 11ed6e2a4b741..08be25e0937aa 100644 --- a/lib/web_ui/test/canvaskit/image_test.dart +++ b/lib/web_ui/test/canvaskit/image_test.dart @@ -38,6 +38,27 @@ void testMain() { expect(image.box.isDeleted, true); }); + test('CkAnimatedImage can be cloned and explicitly disposed of', () async { + final SkAnimatedImage skAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final CkAnimatedImage image = CkAnimatedImage(skAnimatedImage); + final CkAnimatedImage imageClone = image.clone(); + + expect(image.isCloneOf(imageClone), true); + expect(image.box.isDeleted, false); + await Future.delayed(Duration.zero); + expect(skAnimatedImage.isDeleted(), false); + image.dispose(); + expect(image.box.isDeleted, true); + expect(imageClone.box.isDeleted, false); + await Future.delayed(Duration.zero); + expect(skAnimatedImage.isDeleted(), false); + imageClone.dispose(); + expect(image.box.isDeleted, true); + expect(imageClone.box.isDeleted, true); + await Future.delayed(Duration.zero); + expect(skAnimatedImage.isDeleted(), true); + }); + test('CkImage toString', () { final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame(); final CkImage image = CkImage(skImage); @@ -54,6 +75,27 @@ void testMain() { image.dispose(); expect(image.box.isDeleted, true); }); + + test('CkImage can be explicitly disposed of when cloned', () async { + final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage).getCurrentFrame(); + final CkImage image = CkImage(skImage); + final CkImage imageClone = image.clone(); + + expect(image.isCloneOf(imageClone), true); + expect(image.box.isDeleted, false); + await Future.delayed(Duration.zero); + expect(skImage.isDeleted(), false); + image.dispose(); + expect(image.box.isDeleted, true); + expect(imageClone.box.isDeleted, false); + await Future.delayed(Duration.zero); + expect(skImage.isDeleted(), false); + imageClone.dispose(); + expect(image.box.isDeleted, true); + expect(imageClone.box.isDeleted, true); + await Future.delayed(Duration.zero); + expect(skImage.isDeleted(), true); + }); // TODO: https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } diff --git a/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart b/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart index 08e0cb3d55837..a47246c24894b 100644 --- a/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart +++ b/lib/web_ui/test/canvaskit/skia_objects_cache_test.dart @@ -12,6 +12,7 @@ import 'package:ui/ui.dart' as ui; import 'package:ui/src/engine.dart'; import 'common.dart'; +import '../matchers.dart'; void main() { internalBootstrapBrowserTest(() => testMain); @@ -153,9 +154,49 @@ void _tests() { expect(SkiaObjects.oneShotCache.debugContains(object2), isFalse); }); }); + + + group(SkiaObjectBox, () { + test('Records stack traces and respects refcounts', () async { + TestOneShotSkiaObject.deleteCount = 0; + final TestOneShotSkiaObject skObject = TestOneShotSkiaObject(); + final Object wrapper = Object(); + final SkiaObjectBox box = SkiaObjectBox(wrapper, skObject); + + expect(box.debugGetStackTraces().length, 1); + + final SkiaObjectBox clone = box.clone(wrapper); + expect(clone, isNot(same(box))); + expect(clone.debugGetStackTraces().length, 2); + expect(box.debugGetStackTraces().length, 2); + + box.delete(); + + expect(() => box.clone(wrapper), throwsAssertionError); + + expect(box.isDeleted, true); + + // Let any timers elapse. + await Future.delayed(Duration.zero); + expect(TestOneShotSkiaObject.deleteCount, 0); + + expect(clone.debugGetStackTraces().length, 1); + expect(box.debugGetStackTraces().length, 1); + + clone.delete(); + expect(() => clone.clone(wrapper), throwsAssertionError); + + // Let any timers elapse. + await Future.delayed(Duration.zero); + expect(TestOneShotSkiaObject.deleteCount, 1); + + expect(clone.debugGetStackTraces().length, 0); + expect(box.debugGetStackTraces().length, 0); + }); + }); } -class TestOneShotSkiaObject extends OneShotSkiaObject { +class TestOneShotSkiaObject extends OneShotSkiaObject implements SkDeletable { static int deleteCount = 0; TestOneShotSkiaObject() : super(SkPaint()); From fb19b87175959da0383c25ffa7440d10efb15b87 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Fri, 2 Oct 2020 16:47:01 -0400 Subject: [PATCH 25/31] Roll Skia from 8cc5f19f439d to b509bbb81f36 (3 revisions) (#21568) --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 73974973df9d5..800727d315d4f 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': '8cc5f19f439d31e832b137664b75b11607096eb3', + 'skia_revision': 'b509bbb81f36f6cd17da0f2e90b724d379de43de', # 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 4419b99ea9999..f3795a8863020 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: ab0c5248141d05498ef0e32b2ef80454 +Signature: 13611a33f6e2badc11a90c3a3f0467e6 UNUSED LICENSES: From 69cbb2bbb834065d8c37419328b802d49efb8566 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Fri, 2 Oct 2020 13:51:50 -0700 Subject: [PATCH 26/31] iOS Text Editing Infinite Loop (#20160) Fixes an infinite loop by eliminating an unnecessary engine/framework message. --- .../Source/FlutterTextInputPlugin.mm | 12 +-- .../Source/FlutterTextInputPluginTest.m | 82 +++++++------------ 2 files changed, 30 insertions(+), 64 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index 71f5354c4b64d..507b9cae8434a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -537,10 +537,7 @@ - (void)setTextInputClient:(int)client { _textInputClient = client; } -// Return true if the new input state needs to be synced back to the framework. -// TODO(LongCatIsLooong): setTextInputState should never call updateEditingState. Sending the -// editing value back may overwrite the framework's updated editing value. -- (BOOL)setTextInputState:(NSDictionary*)state { +- (void)setTextInputState:(NSDictionary*)state { NSString* newText = state[@"text"]; BOOL textChanged = ![self.text isEqualToString:newText]; if (textChanged) { @@ -575,9 +572,6 @@ - (BOOL)setTextInputState:(NSDictionary*)state { if (textChanged) { [self.inputDelegate textDidChange:self]; } - - // For consistency with Android behavior, send an update to the framework if the text changed. - return textChanged; } // Extracts the selection information from the editing state dictionary. @@ -1423,9 +1417,7 @@ - (void)addToInputParentViewIfNeeded:(FlutterTextInputView*)inputView { } - (void)setTextInputEditingState:(NSDictionary*)state { - if ([_activeView setTextInputState:state]) { - [_activeView updateEditingState]; - } + [_activeView setTextInputState:state]; } - (void)clearTextInputClient { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m index 432ce718bf31a..88683abb087f6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m @@ -16,7 +16,7 @@ @interface FlutterTextInputView () @property(nonatomic, copy) NSString* autofillId; - (void)setEditableTransform:(NSArray*)matrix; -- (BOOL)setTextInputState:(NSDictionary*)state; +- (void)setTextInputState:(NSDictionary*)state; - (void)setMarkedRect:(CGRect)markedRect; - (void)updateEditingState; - (BOOL)isVisibleToAutofill; @@ -211,71 +211,45 @@ - (void)testUITextInputCallsUpdateEditingStateOnce { XCTAssertEqual(updateCount, 6); } -- (void)testTextChangesTriggerUpdateEditingClient { +- (void)testTextChangesDoNotTriggerUpdateEditingClient { FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init]; inputView.textInputDelegate = engine; - [inputView.text setString:@"BEFORE"]; - inputView.markedTextRange = nil; - inputView.selectedTextRange = nil; - - // Text changes trigger update. - XCTAssertTrue([inputView setTextInputState:@{@"text" : @"AFTER"}]); - - // Don't send anything if there's nothing new. - XCTAssertFalse([inputView setTextInputState:@{@"text" : @"AFTER"}]); -} + __block int updateCount = 0; + OCMStub([engine updateEditingClient:0 withState:[OCMArg isNotNil]]) + .andDo(^(NSInvocation* invocation) { + updateCount++; + }); -- (void)testSelectionChangeDoesNotTriggerUpdateEditingClient { - FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init]; - inputView.textInputDelegate = engine; + [inputView.text setString:@"BEFORE"]; + XCTAssertEqual(updateCount, 0); - [inputView.text setString:@"SELECTION"]; inputView.markedTextRange = nil; inputView.selectedTextRange = nil; + XCTAssertEqual(updateCount, 1); - BOOL shouldUpdate = [inputView - setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @0, @"selectionExtent" : @3}]; - XCTAssertFalse(shouldUpdate); + // Text changes don't trigger an update. + XCTAssertEqual(updateCount, 1); + [inputView setTextInputState:@{@"text" : @"AFTER"}]; + XCTAssertEqual(updateCount, 1); + [inputView setTextInputState:@{@"text" : @"AFTER"}]; + XCTAssertEqual(updateCount, 1); - shouldUpdate = [inputView + // Selection changes don't trigger an update. + [inputView + setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @0, @"selectionExtent" : @3}]; + XCTAssertEqual(updateCount, 1); + [inputView setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @3}]; - XCTAssertFalse(shouldUpdate); - - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}]; - XCTAssertFalse(shouldUpdate); - - // Don't send anything if there's nothing new. - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"SELECTION", @"selectionBase" : @1, @"selectionExtent" : @2}]; - XCTAssertFalse(shouldUpdate); -} - -- (void)testComposingChangeDoesNotTriggerUpdateEditingClient { - FlutterTextInputView* inputView = [[FlutterTextInputView alloc] init]; - inputView.textInputDelegate = engine; - - // Reset to test marked text. - [inputView.text setString:@"COMPOSING"]; - inputView.markedTextRange = nil; - inputView.selectedTextRange = nil; - - BOOL shouldUpdate = [inputView - setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @0, @"composingExtent" : @3}]; - XCTAssertFalse(shouldUpdate); - - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}]; - XCTAssertFalse(shouldUpdate); - - shouldUpdate = [inputView - setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}]; - XCTAssertFalse(shouldUpdate); + XCTAssertEqual(updateCount, 1); - shouldUpdate = [inputView + // Composing region changes don't trigger an update. + [inputView setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @2}]; - XCTAssertFalse(shouldUpdate); + XCTAssertEqual(updateCount, 1); + [inputView + setTextInputState:@{@"text" : @"COMPOSING", @"composingBase" : @1, @"composingExtent" : @3}]; + XCTAssertEqual(updateCount, 1); } - (void)testUITextInputAvoidUnnecessaryUndateEditingClientCalls { From 3c0da65c0cbf4965d8e0db55ea4137cf17713801 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 2 Oct 2020 14:09:07 -0700 Subject: [PATCH 27/31] Allow hot reload without syncing all asset files to devFS (#21436) Always add the asset resolver from the configuration settings directory when restarting or recreating the asset manager --- shell/common/fixtures/shell_test.dart | 16 +++++++++ shell/common/shell.cc | 14 ++++++++ shell/common/shell.h | 5 +++ shell/common/shell_unittests.cc | 49 +++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 75fad7e109283..492ddb8960d03 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -163,3 +163,19 @@ void localtimesMatch() { // formatting since package:intl is not available. notifyLocalTime(timeStr.split(":")[0]); } + +void notifyCanAccessResource(bool success) native 'NotifyCanAccessResource'; + +void notifySetAssetBundlePath() native 'NotifySetAssetBundlePath'; + +@pragma('vm:entry-point') +void canAccessResourceFromAssetDir() async { + notifySetAssetBundlePath(); + window.sendPlatformMessage( + 'flutter/assets', + Uint8List.fromList(utf8.encode('kernel_blob.bin')).buffer.asByteData(), + (ByteData byteData) { + notifyCanAccessResource(byteData != null); + }, + ); +} diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 1503519ca7f4d..2da9e9786e385 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1404,6 +1404,7 @@ bool Shell::OnServiceProtocolRunInView( configuration.AddAssetResolver( std::make_unique(fml::OpenDirectory( asset_directory_path.c_str(), false, fml::FilePermission::kRead))); + configuration.AddAssetResolver(RestoreOriginalAssetResolver()); auto& allocator = response->GetAllocator(); response->SetObject(); @@ -1516,6 +1517,7 @@ bool Shell::OnServiceProtocolSetAssetBundlePath( auto asset_manager = std::make_shared(); + asset_manager->PushFront(RestoreOriginalAssetResolver()); asset_manager->PushFront(std::make_unique( fml::OpenDirectory(params.at("assetDirectory").data(), false, fml::FilePermission::kRead))); @@ -1622,4 +1624,16 @@ void Shell::OnDisplayUpdates(DisplayUpdateType update_type, display_manager_->HandleDisplayUpdates(update_type, displays); } +// Add the original asset directory to the resolvers so that unmodified assets +// bundled with the application specific format (APK, IPA) can be used without +// syncing to the Dart devFS. +std::unique_ptr Shell::RestoreOriginalAssetResolver() { + if (fml::UniqueFD::traits_type::IsValid(settings_.assets_dir)) { + return std::make_unique( + fml::Duplicate(settings_.assets_dir)); + } + return std::make_unique(fml::OpenDirectory( + settings_.assets_path.c_str(), false, fml::FilePermission::kRead)); +}; + } // namespace flutter diff --git a/shell/common/shell.h b/shell/common/shell.h index 74ad1c280e3ae..a345b0461e5fc 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -10,6 +10,7 @@ #include #include +#include "flutter/assets/directory_asset_bundle.h" #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" #include "flutter/flow/surface.h" @@ -612,6 +613,10 @@ class Shell final : public PlatformView::Delegate, const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response); + // Creates an asset bundle from the original settings asset path or + // directory. + std::unique_ptr RestoreOriginalAssetResolver(); + // 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_unittests.cc b/shell/common/shell_unittests.cc index 2e708d0cbc612..fbf0647eb1191 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -2135,5 +2135,54 @@ TEST_F(ShellTest, IgnoresInvalidMetrics) { DestroyShell(std::move(shell), std::move(task_runners)); } +TEST_F(ShellTest, OnServiceProtocolSetAssetBundlePathWorks) { + Settings settings = CreateSettingsForFixture(); + std::unique_ptr shell = CreateShell(settings); + RunConfiguration configuration = + RunConfiguration::InferFromSettings(settings); + configuration.SetEntrypoint("canAccessResourceFromAssetDir"); + + // Verify isolate can load a known resource with the + // default asset directory - kernel_blob.bin + fml::AutoResetWaitableEvent latch; + + // Callback used to signal whether the resource was loaded successfully. + bool can_access_resource = false; + auto native_can_access_resource = [&can_access_resource, + &latch](Dart_NativeArguments args) { + Dart_Handle exception = nullptr; + can_access_resource = + tonic::DartConverter::FromArguments(args, 0, exception); + latch.Signal(); + }; + AddNativeCallback("NotifyCanAccessResource", + CREATE_NATIVE_ENTRY(native_can_access_resource)); + + // Callback used to delay the asset load until after the service + // protocol method has finished. + auto native_notify_set_asset_bundle_path = + [&shell](Dart_NativeArguments args) { + // Update the asset directory to a bonus path. + ServiceProtocol::Handler::ServiceProtocolMap params; + params["assetDirectory"] = "assetDirectory"; + rapidjson::Document document; + OnServiceProtocol(shell.get(), ServiceProtocolEnum::kSetAssetBundlePath, + shell->GetTaskRunners().GetUITaskRunner(), params, + &document); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + document.Accept(writer); + }; + AddNativeCallback("NotifySetAssetBundlePath", + CREATE_NATIVE_ENTRY(native_notify_set_asset_bundle_path)); + + RunEngine(shell.get(), std::move(configuration)); + + latch.Wait(); + ASSERT_TRUE(can_access_resource); + + DestroyShell(std::move(shell)); +} + } // namespace testing } // namespace flutter From af899d6529cae03e0d586a1f762c9bce921421c0 Mon Sep 17 00:00:00 2001 From: Sanjay Chouksey Date: Fri, 2 Oct 2020 14:20:50 -0700 Subject: [PATCH 28/31] Adds response to view requestFocus platformview message (#21550) This change adds a response to the platform view message: requestFocus This allows the caller to handle the case when the request fails with an error value. Co-authored-by: Sanjay Chouksey --- .../platform/fuchsia/flutter/platform_view.cc | 19 ++- .../fuchsia/flutter/platform_view_unittest.cc | 118 +++++++++++++++++- 2 files changed, 131 insertions(+), 6 deletions(-) diff --git a/shell/platform/fuchsia/flutter/platform_view.cc b/shell/platform/fuchsia/flutter/platform_view.cc index 3f15dac57da37..1e5fdd0082611 100644 --- a/shell/platform/fuchsia/flutter/platform_view.cc +++ b/shell/platform/fuchsia/flutter/platform_view.cc @@ -831,10 +831,21 @@ void PlatformView::HandleFlutterPlatformViewsChannelPlatformMessage( }); focuser_->RequestFocus( std::move(ref), - [view_ref = view_ref->value.GetUint64()]( - fuchsia::ui::views::Focuser_RequestFocus_Result result) { - if (result.is_err()) { - FML_LOG(ERROR) << "Failed to request focus for view: " << view_ref; + [view_ref = view_ref->value.GetUint64(), + message](fuchsia::ui::views::Focuser_RequestFocus_Result result) { + if (message->response().get()) { + int result_code = + result.is_err() + ? static_cast< + std::underlying_type_t>( + result.err()) + : 0; + + std::ostringstream out; + out << "[" << result_code << "]"; + message->response()->Complete( + std::make_unique( + (const uint8_t*)out.str().c_str(), out.str().length())); } }); } else { diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 1810c4cacac01..de2de6468fa92 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -18,6 +18,7 @@ #include "flutter/flow/embedded_views.h" #include "flutter/lib/ui/window/platform_message.h" #include "flutter/lib/ui/window/window.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "surface.h" @@ -131,14 +132,28 @@ class MockFocuser : public fuchsia::ui::views::Focuser { ~MockFocuser() override = default; bool request_focus_called = false; + bool fail_request_focus = false; private: void RequestFocus(fuchsia::ui::views::ViewRef view_ref, RequestFocusCallback callback) override { request_focus_called = true; + auto result = + fail_request_focus + ? fuchsia::ui::views::Focuser_RequestFocus_Result::WithErr( + fuchsia::ui::views::Error::DENIED) + : fuchsia::ui::views::Focuser_RequestFocus_Result::WithResponse( + fuchsia::ui::views::Focuser_RequestFocus_Response()); + callback(std::move(result)); } }; +class MockResponse : public flutter::PlatformMessageResponse { + public: + MOCK_METHOD1(Complete, void(std::unique_ptr data)); + MOCK_METHOD0(CompleteEmpty, void()); +}; + TEST_F(PlatformViewTests, ChangesAccessibilitySettings) { sys::testing::ServiceDirectoryProvider services_provider(dispatcher()); @@ -513,16 +528,115 @@ TEST_F(PlatformViewTests, RequestFocusTest) { "}", b.get()); + // Define a custom gmock matcher to capture the response to platform message. + struct DataArg { + void Complete(std::unique_ptr data) { + this->data = std::move(data); + } + std::unique_ptr data; + }; + DataArg data_arg; + fml::RefPtr response = fml::MakeRefCounted(); + EXPECT_CALL(*response, Complete(testing::_)) + .WillOnce(testing::Invoke(&data_arg, &DataArg::Complete)); + fml::RefPtr message = fml::MakeRefCounted( "flutter/platform_views", - std::vector(buff, buff + sizeof(buff)), - fml::RefPtr()); + std::vector(buff, buff + sizeof(buff)), response); + base_view->HandlePlatformMessage(message); + + RunLoopUntilIdle(); + + EXPECT_TRUE(mock_focuser.request_focus_called); + auto result = std::string((const char*)data_arg.data->GetMapping(), + data_arg.data->GetSize()); + EXPECT_EQ(std::string("[0]"), result); +} + +// Test to make sure that PlatformView correctly registers messages sent on +// the "flutter/platform_views" channel, correctly parses the JSON it receives +// and calls the focuser's RequestFocus with the appropriate args. +TEST_F(PlatformViewTests, RequestFocusFailTest) { + sys::testing::ServiceDirectoryProvider services_provider(dispatcher()); + MockPlatformViewDelegate delegate; + zx::eventpair a, b; + zx::eventpair::create(/* flags */ 0u, &a, &b); + auto view_ref = fuchsia::ui::views::ViewRef({ + .reference = std::move(a), + }); + flutter::TaskRunners task_runners = + flutter::TaskRunners("test_runners", nullptr, nullptr, nullptr, nullptr); + + MockFocuser mock_focuser; + mock_focuser.fail_request_focus = true; + fidl::BindingSet focuser_bindings; + auto focuser_handle = focuser_bindings.AddBinding(&mock_focuser); + + auto platform_view = flutter_runner::PlatformView( + delegate, // delegate + "test_platform_view", // label + std::move(view_ref), // view_refs + std::move(task_runners), // task_runners + services_provider.service_directory(), // runner_services + nullptr, // parent_environment_service_provider_handle + nullptr, // session_listener_request + std::move(focuser_handle), // focuser, + nullptr, // on_session_listener_error_callback + nullptr, // on_enable_wireframe_callback, + nullptr, // on_create_view_callback, + nullptr, // on_update_view_callback, + nullptr, // on_destroy_view_callback, + nullptr, // on_create_surface_callback, + fml::TimeDelta::Zero(), // vsync_offset + ZX_HANDLE_INVALID // vsync_event_handle + ); + + // Cast platform_view to its base view so we can have access to the public + // "HandlePlatformMessage" function. + auto base_view = dynamic_cast(&platform_view); + EXPECT_TRUE(base_view); + + // JSON for the message to be passed into the PlatformView. + char buff[254]; + snprintf(buff, sizeof(buff), + "{" + " \"method\":\"View.requestFocus\"," + " \"args\": {" + " \"viewRef\":%u" + " }" + "}", + b.get()); + + // Define a custom gmock matcher to capture the response to platform message. + struct DataArg { + void Complete(std::unique_ptr data) { + this->data = std::move(data); + } + std::unique_ptr data; + }; + DataArg data_arg; + fml::RefPtr response = fml::MakeRefCounted(); + EXPECT_CALL(*response, Complete(testing::_)) + .WillOnce(testing::Invoke(&data_arg, &DataArg::Complete)); + + fml::RefPtr message = + fml::MakeRefCounted( + "flutter/platform_views", + std::vector(buff, buff + sizeof(buff)), response); base_view->HandlePlatformMessage(message); RunLoopUntilIdle(); EXPECT_TRUE(mock_focuser.request_focus_called); + auto result = std::string((const char*)data_arg.data->GetMapping(), + data_arg.data->GetSize()); + std::ostringstream out; + out << "[" + << static_cast>( + fuchsia::ui::views::Error::DENIED) + << "]"; + EXPECT_EQ(out.str(), result); } // Test to make sure that PlatformView correctly returns a Surface instance From 36e33e2ed93a2085da45fb257c81cc585e64518d Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Fri, 18 Sep 2020 15:56:50 -0700 Subject: [PATCH 29/31] support uri intent launcher in android --- .../embedding/android/FlutterActivity.java | 10 +++++++-- .../android/FlutterActivityTest.java | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 66c2acd8e886b..dcb878afdcf5c 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -26,6 +26,7 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.View; @@ -782,8 +783,13 @@ public String getDartEntrypointFunctionName() { */ @NonNull public String getInitialRoute() { - if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) { - return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE); + Intent intent = getIntent(); + Uri data = intent.getData(); + if (data != null && data.getPath() != null && !data.getPath().isEmpty()) { + return data.getPath(); + } + if (intent.hasExtra(EXTRA_INITIAL_ROUTE)) { + return intent.getStringExtra(EXTRA_INITIAL_ROUTE); } try { diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java index 2fe23de49b303..6ebd9c36f4d56 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java @@ -13,6 +13,7 @@ import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -116,6 +117,26 @@ public void itCreatesNewEngineIntentWithRequestedSettings() { assertEquals(TransparencyMode.transparent, flutterActivity.getTransparencyMode()); } + @Test + public void itCreatesNewEngineIntentWithLaunchUri() { + Intent intent = FlutterActivity.createDefaultIntent(RuntimeEnvironment.application); + intent.setData(Uri.parse("http://myApp/custom/route")); + ActivityController activityController = + Robolectric.buildActivity(FlutterActivity.class, intent); + FlutterActivity flutterActivity = activityController.get(); + flutterActivity.setDelegate(new FlutterActivityAndFragmentDelegate(flutterActivity)); + + assertEquals("main", flutterActivity.getDartEntrypointFunctionName()); + assertEquals("/custom/route", flutterActivity.getInitialRoute()); + assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray()); + assertTrue(flutterActivity.shouldAttachEngineToActivity()); + assertNull(flutterActivity.getCachedEngineId()); + assertTrue(flutterActivity.shouldDestroyEngineWithHost()); + assertEquals(BackgroundMode.opaque, flutterActivity.getBackgroundMode()); + assertEquals(RenderMode.surface, flutterActivity.getRenderMode()); + assertEquals(TransparencyMode.opaque, flutterActivity.getTransparencyMode()); + } + @Test public void itCreatesCachedEngineIntentThatDoesNotDestroyTheEngine() { Intent intent = From 818dcc35413a3e4a2bad201fc9b4964c562e1a67 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Wed, 30 Sep 2020 14:46:23 -0700 Subject: [PATCH 30/31] addressing comment --- .../flutter/embedding/android/FlutterActivity.java | 4 ++-- .../embedding/android/FlutterFragmentActivity.java | 10 ++++++++-- .../embedding/android/FlutterActivityTest.java | 2 +- .../android/FlutterFragmentActivityTest.java | 14 ++++++++++++++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index dcb878afdcf5c..9af292439a93b 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -785,8 +785,8 @@ public String getDartEntrypointFunctionName() { public String getInitialRoute() { Intent intent = getIntent(); Uri data = intent.getData(); - if (data != null && data.getPath() != null && !data.getPath().isEmpty()) { - return data.getPath(); + if (data != null && !data.toString().isEmpty()) { + return data.toString(); } if (intent.hasExtra(EXTRA_INITIAL_ROUTE)) { return intent.getStringExtra(EXTRA_INITIAL_ROUTE); diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index ff15581a57b22..882d53daa2cb9 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -24,6 +24,7 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.View; @@ -654,8 +655,13 @@ public String getDartEntrypointFunctionName() { */ @NonNull protected String getInitialRoute() { - if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) { - return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE); + Intent intent = getIntent(); + Uri data = intent.getData(); + if (data != null && !data.toString().isEmpty()) { + return data.toString(); + } + if (intent.hasExtra(EXTRA_INITIAL_ROUTE)) { + return intent.getStringExtra(EXTRA_INITIAL_ROUTE); } try { diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java index 6ebd9c36f4d56..782359d16121f 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java @@ -127,7 +127,7 @@ public void itCreatesNewEngineIntentWithLaunchUri() { flutterActivity.setDelegate(new FlutterActivityAndFragmentDelegate(flutterActivity)); assertEquals("main", flutterActivity.getDartEntrypointFunctionName()); - assertEquals("/custom/route", flutterActivity.getInitialRoute()); + assertEquals("http://myApp/custom/route", flutterActivity.getInitialRoute()); assertArrayEquals(new String[] {}, flutterActivity.getFlutterShellArgs().toArray()); assertTrue(flutterActivity.shouldAttachEngineToActivity()); assertNull(flutterActivity.getCachedEngineId()); diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java index cd311a835588e..dcf086f8c4c93 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java @@ -7,6 +7,7 @@ import android.content.Context; import android.content.Intent; +import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode; @@ -19,8 +20,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.android.controller.ActivityController; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @Config(manifest = Config.NONE) @@ -82,6 +85,17 @@ public void itRegistersPluginsAtConfigurationTime() { assertEquals(activity.getFlutterEngine(), registeredEngines.get(0)); } + @Test + public void itSetsInitialRouteFromTheLaunchURI() { + Intent intent = FlutterFragmentActivity.createDefaultIntent(RuntimeEnvironment.application); + intent.setData(Uri.parse("http://myApp/custom/route")); + ActivityController activityController = + Robolectric.buildActivity(FlutterFragmentActivity.class, intent); + FlutterFragmentActivity flutterActivity = activityController.get(); + + assertEquals("http://myApp/custom/route", flutterActivity.getInitialRoute()); + } + static class FlutterFragmentActivityWithProvidedEngine extends FlutterFragmentActivity { @Override protected FlutterFragment createFlutterFragment() { From cfb085cc591788a1acbcbb0915f1163ab3b85648 Mon Sep 17 00:00:00 2001 From: Chun-Heng Tai Date: Thu, 1 Oct 2020 11:25:28 -0700 Subject: [PATCH 31/31] Android sends push route when onNewIntent --- .../FlutterActivityAndFragmentDelegate.java | 7 ++++++- ...lutterActivityAndFragmentDelegateTest.java | 20 +++++++++++++++++++ .../android/FlutterFragmentActivityTest.java | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index 78585172dd683..e299f79401c52 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -9,6 +9,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; @@ -622,8 +623,12 @@ void onRequestPermissionsResult( void onNewIntent(@NonNull Intent intent) { ensureAlive(); if (flutterEngine != null) { - Log.v(TAG, "Forwarding onNewIntent() to FlutterEngine."); + Log.v(TAG, "Forwarding onNewIntent() to FlutterEngine and sending pushRoute message."); flutterEngine.getActivityControlSurface().onNewIntent(intent); + Uri data = intent.getData(); + if (data != null && !data.toString().isEmpty()) { + flutterEngine.getNavigationChannel().pushRoute(intent.getData().toString()); + } } else { Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Activity."); } 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 4a621b3344107..00742a6624c08 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -15,6 +15,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.net.Uri; import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; import io.flutter.FlutterInjector; @@ -426,6 +427,25 @@ public void itForwardsOnRequestPermissionsResultToFlutterEngine() { .onRequestPermissionsResult(any(Integer.class), any(String[].class), any(int[].class)); } + @Test + public void itSendsPushRouteMessageWhenOnNewIntent() { + // Create the real object that we're testing. + FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost); + + // --- Execute the behavior under test --- + // The FlutterEngine is setup in onAttach(). + delegate.onAttach(RuntimeEnvironment.application); + + Intent mockIntent = mock(Intent.class); + when(mockIntent.getData()).thenReturn(Uri.parse("http://myApp/custom/route")); + // Emulate the host and call the method that we expect to be forwarded. + delegate.onNewIntent(mockIntent); + + // Verify that the navigation channel was given the push route message. + verify(mockFlutterEngine.getNavigationChannel(), times(1)) + .pushRoute("http://myApp/custom/route"); + } + @Test public void itForwardsOnNewIntentToFlutterEngine() { // Create the real object that we're testing. diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java index dcf086f8c4c93..1edd8c1734a5f 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentActivityTest.java @@ -20,10 +20,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.android.controller.ActivityController; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; @Config(manifest = Config.NONE)