diff --git a/.cirrus.yml b/.cirrus.yml index c38fbb5b85941..447d02fc70dac 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -3,9 +3,10 @@ gcp_credentials: ENCRYPTED[987a78af29b91ce8489594c9ab3fec21845bbe5ba68294b8f6def web_shard_template: &WEB_SHARD_TEMPLATE only_if: "changesInclude('.cirrus.yml', 'DEPS', 'lib/web_ui/**', 'web_sdk/**') || $CIRRUS_PR == ''" environment: - # As of March 2020, the Web shards needed 16G of RAM and 4 CPUs to run all framework tests with goldens without flaking. + # As of March 2020, the Web shards needed 16G of RAM and 4 CPUs to run all framework tests with goldens without flaking. CPU: 4 MEMORY: 16G + WEB_SHARD_COUNT: 4 compile_host_script: | cd $ENGINE_PATH/src ./flutter/tools/gn --unoptimized --full-dart-sdk @@ -124,19 +125,7 @@ task: - name: web_tests-2-linux << : *WEB_SHARD_TEMPLATE - - name: web_tests-3-linux - << : *WEB_SHARD_TEMPLATE - - - name: web_tests-4-linux - << : *WEB_SHARD_TEMPLATE - - - name: web_tests-5-linux - << : *WEB_SHARD_TEMPLATE - - - name: web_tests-6-linux - << : *WEB_SHARD_TEMPLATE - - - name: web_tests-7_last-linux # last Web shard must end with _last + - name: web_tests-3_last-linux # last Web shard must end with _last << : *WEB_SHARD_TEMPLATE - name: web_engine_analysis @@ -207,8 +196,3 @@ task: build_script: | cd $ENGINE_PATH/src/flutter ./ci/build.sh - - name: build_fuchsia_artifacts - compile_fuchsia_script: | - cd $ENGINE_PATH/src - ./flutter/tools/fuchsia/build_fuchsia_artifacts.py --engine-version HEAD --runtime-mode debug --no-lto --archs x64 - cd $ENGINE_PATH/src/flutter diff --git a/.github/auto_assign.yml b/.github/auto_assign.yml index 638c686bd5cdc..2c9c96cbe1a1c 100644 --- a/.github/auto_assign.yml +++ b/.github/auto_assign.yml @@ -17,7 +17,6 @@ reviewers: - GaryQian - jason-simmons - iskakaushik - - franciscojma86 - cbracken - flar diff --git a/BUILD.gn b/BUILD.gn index fea2a2b5bebf1..74f58bfdad7a1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//flutter/common/config.gni") +import("//flutter/shell/platform/config.gni") if (is_fuchsia) { import("//build/fuchsia/sdk.gni") @@ -63,10 +64,8 @@ group("flutter") { # If on the host, compile all unittests targets. if (current_toolchain == host_toolchain) { if (is_mac) { - public_deps += [ - "//flutter/shell/platform/darwin:flutter_channels_unittests", - "//flutter/shell/platform/darwin/macos:flutter_desktop_darwin_unittests", - ] + public_deps += + [ "//flutter/shell/platform/darwin:flutter_channels_unittests" ] } public_deps += [ @@ -75,22 +74,11 @@ group("flutter") { "//flutter/lib/ui:ui_unittests", "//flutter/runtime:runtime_unittests", "//flutter/shell/common:shell_unittests", - "//flutter/shell/platform/common/cpp:common_cpp_core_unittests", - "//flutter/shell/platform/common/cpp:common_cpp_unittests", - "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper_unittests", "//flutter/shell/platform/embedder:embedder_unittests", - "//flutter/shell/platform/glfw/client_wrapper:client_wrapper_glfw_unittests", "//flutter/testing:testing_unittests", "//flutter/third_party/txt:txt_unittests", ] - if (is_win) { - public_deps += [ - "//flutter/shell/platform/windows:flutter_windows_unittests", - "//flutter/shell/platform/windows/client_wrapper:client_wrapper_windows_unittests", - ] - } - if (!is_win) { public_deps += [ "//flutter/fml:fml_benchmarks", @@ -98,6 +86,26 @@ group("flutter") { "//flutter/third_party/txt:txt_benchmarks", ] } + + # Unit tests for desktop embeddings should only be built if the desktop + # embeddings are being built. + if (enable_desktop_embeddings) { + public_deps += [ + "//flutter/shell/platform/common/cpp:common_cpp_core_unittests", + "//flutter/shell/platform/common/cpp:common_cpp_unittests", + "//flutter/shell/platform/common/cpp/client_wrapper:client_wrapper_unittests", + "//flutter/shell/platform/glfw/client_wrapper:client_wrapper_glfw_unittests", + ] + if (is_mac) { + public_deps += [ "//flutter/shell/platform/darwin/macos:flutter_desktop_darwin_unittests" ] + } + if (is_win) { + public_deps += [ + "//flutter/shell/platform/windows:flutter_windows_unittests", + "//flutter/shell/platform/windows/client_wrapper:client_wrapper_windows_unittests", + ] + } + } } } diff --git a/DEPS b/DEPS index 1af4d736d7562..c071e16aa4a62 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': 'ad653d8378d7a17502956c4addebb68eb3129961', + 'skia_revision': 'efebaa2a1152d3950f252d63a619f53992366ff2', # When updating the Dart revision, ensure that all entries that are # dependencies of Dart are also updated to match the entries in the @@ -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': '3e43a3dcadf96c0f1e30b12e0a1805df5a336c3c', + 'dart_revision': '5900a0ac492b6d8ac2ca45a63f9236c61aa3f6a9', # WARNING: DO NOT EDIT MANUALLY # The lines between blank lines above and below are generated by a script. See create_updated_flutter_deps.py @@ -62,7 +62,7 @@ vars = { 'dart_http_throttle_tag': '1.0.2', 'dart_intl_tag': '0.16.1', 'dart_json_rpc_2_tag': '2.0.9', - 'dart_linter_tag': '0.1.115', + 'dart_linter_tag': '0.1.114', 'dart_logging_tag': '0.11.3+2', 'dart_markdown_tag': '2.1.1', 'dart_matcher_tag': '0.12.5', @@ -70,9 +70,8 @@ vars = { 'dart_mockito_tag': 'd39ac507483b9891165e422ec98d9fb480037c8b', 'dart_mustache_tag': '5e81b12215566dbe2473b2afd01a8a8aedd56ad9', 'dart_oauth2_tag': '1.2.1', - 'dart_observatory_pub_packages_rev': '0894122173b0f98eb08863a7712e78407d4477bc', 'dart_package_config_tag': 'v1.9.2', - 'dart_path_tag': '1.6.2', + 'dart_path_tag': '1.7.0', 'dart_pedantic_tag': 'v1.9.0', 'dart_pool_tag': '1.3.6', 'dart_protobuf_rev': '3746c8fd3f2b0147623a8e3db89c3ff4330de760', @@ -139,7 +138,7 @@ allowed_hosts = [ ] deps = { - 'src': 'https://github.com/flutter/buildroot.git' + '@' + '036715c76da60220b39312ea066cd65d32c2157d', + 'src': 'https://github.com/flutter/buildroot.git' + '@' + '1b6a1b344074a1105b2e9b1c714766bace22acd7', # Fuchsia compatibility # @@ -196,9 +195,6 @@ deps = { 'src/third_party/dart/pkg/analysis_server/language_model': {'packages': [{'version': 'lIRt14qoA1Cocb8j3yw_Fx5cfYou2ddam6ArBm4AI6QC', 'package': 'dart/language_model'}], 'dep_type': 'cipd'}, - 'src/third_party/dart/third_party/observatory_pub_packages': - Var('dart_git') + '/observatory_pub_packages.git' + '@' + Var('dart_observatory_pub_packages_rev'), - 'src/third_party/dart/third_party/pkg/args': Var('dart_git') + '/args.git' + '@' + Var('dart_args_tag'), @@ -233,7 +229,7 @@ deps = { Var('dart_git') + '/dart2js_info.git' + '@' + Var('dart_dart2js_info_tag'), 'src/third_party/dart/third_party/pkg/dartdoc': - Var('dart_git') + '/dartdoc.git@v0.30.3', + Var('dart_git') + '/dartdoc.git@v0.30.4', 'src/third_party/dart/third_party/pkg/ffi': Var('dart_git') + '/ffi.git' + '@' + Var('dart_ffi_tag'), @@ -538,7 +534,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/mac-amd64', - 'version': '8JtFK64mmIC2zTEj9ICMrcQBITqKDZVQluLVKczro9kC' + 'version': 'W1XGOOg536oZa9c08cv4pRp2Hq60gagWtr-TMSb6iN0C' } ], 'condition': 'host_os == "mac"', @@ -558,7 +554,7 @@ deps = { 'packages': [ { 'package': 'fuchsia/sdk/core/linux-amd64', - 'version': 'LnaL23_DpQsbnbs-byJi-UoGe1XerKCfLjb4_XkxMRoC' + 'version': 'PgthiSmMqgeVVi3s8ATyJ1ut411xGyk0h0lDZta1iUQC' } ], 'condition': 'host_os == "linux"', diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index efcc72ebf43e9..7340ce6a04007 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1182,6 +1182,9 @@ FILE: ../../../flutter/shell/platform/windows/keyboard_hook_handler.h FILE: ../../../flutter/shell/platform/windows/platform_handler.cc FILE: ../../../flutter/shell/platform/windows/platform_handler.h FILE: ../../../flutter/shell/platform/windows/public/flutter_windows.h +FILE: ../../../flutter/shell/platform/windows/string_conversion.cc +FILE: ../../../flutter/shell/platform/windows/string_conversion.h +FILE: ../../../flutter/shell/platform/windows/string_conversion_unittests.cc FILE: ../../../flutter/shell/platform/windows/text_input_plugin.cc FILE: ../../../flutter/shell/platform/windows/text_input_plugin.h FILE: ../../../flutter/shell/platform/windows/win32_flutter_window.cc diff --git a/ci/licenses_golden/licenses_fuchsia b/ci/licenses_golden/licenses_fuchsia index 5e24346dc2215..eff11642ccd2f 100644 --- a/ci/licenses_golden/licenses_fuchsia +++ b/ci/licenses_golden/licenses_fuchsia @@ -1,4 +1,4 @@ -Signature: adc98c90de1d424da0f0546ff7689965 +Signature: ce7ff857ac8a13447faa8112735366c8 UNUSED LICENSES: @@ -2674,7 +2674,6 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/stream_processor.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/stream_type.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/timeline_function.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.mediacodec/codec_factory.fidl -FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/intent/intent_handler.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/module/module_manifest.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/story/puppet_master.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/story/story_command.fidl @@ -3269,6 +3268,7 @@ FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/activity_reporter.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.media/profile_provider.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.memorypressure/memorypressure.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.modular/session/session_restart_controller.fidl +FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.net/socket.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.settings/night_mode.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/events.fidl FILE: ../../../fuchsia/sdk/linux/fidl/fuchsia.ui.input3/keyboard.fidl diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index 8f0b0639ec3f7..39a234fee7b37 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: 2da5a3733cfc3ee9d4098855a1f98d15 +Signature: bfea63318cfc35e64a47648bbbffcd51 UNUSED LICENSES: @@ -1070,6 +1070,8 @@ FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.ex FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Debug-Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-NoGPU_Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian10-GCC-x86_64-Release-Shared_Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Debug-Chromebook_GLES_Docker.json +FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Debian9-Clang-x86_64-Release-Chromebook_GLES_Docker.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm-Debug-iOS.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-Android_Vulkan.json FILE: ../../../third_party/skia/infra/bots/recipe_modules/build/examples/full.expected/Build-Mac-Clang-arm64-Debug-iOS.json @@ -1237,6 +1239,7 @@ FILE: ../../../third_party/skia/infra/canvaskit/docker/canvaskit-emsdk/Dockerfil FILE: ../../../third_party/skia/infra/config/recipes.cfg FILE: ../../../third_party/skia/infra/cross-compile/docker/cross-linux-arm64/Dockerfile FILE: ../../../third_party/skia/infra/cts/whitelist_devices.json +FILE: ../../../third_party/skia/infra/docker/debian9/Dockerfile FILE: ../../../third_party/skia/infra/gcc/Debian10-mips64el/Dockerfile FILE: ../../../third_party/skia/infra/gcc/Debian10-x86/Dockerfile FILE: ../../../third_party/skia/infra/gcc/Debian10/Dockerfile @@ -3914,11 +3917,17 @@ FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DTypesPriv.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.cpp FILE: ../../../third_party/skia/src/gpu/d3d/GrD3DUtil.h FILE: ../../../third_party/skia/src/gpu/effects/GrDeviceSpaceEffect.fp +FILE: ../../../third_party/skia/src/gpu/effects/GrMatrixEffect.fp FILE: ../../../third_party/skia/src/gpu/effects/generated/GrDeviceSpaceEffect.cpp FILE: ../../../third_party/skia/src/gpu/effects/generated/GrDeviceSpaceEffect.h +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMatrixEffect.cpp +FILE: ../../../third_party/skia/src/gpu/effects/generated/GrMatrixEffect.h +FILE: ../../../third_party/skia/src/gpu/glsl/GrGLSLUniformHandler.cpp FILE: ../../../third_party/skia/src/gpu/vk/GrVkManagedResource.h FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVtoHLSL.cpp FILE: ../../../third_party/skia/src/sksl/SkSLSPIRVtoHLSL.h +FILE: ../../../third_party/skia/src/sksl/SkSLSampleMatrix.cpp +FILE: ../../../third_party/skia/src/sksl/SkSLSampleMatrix.h ---------------------------------------------------------------------------------------------------- Copyright 2020 Google LLC @@ -5033,6 +5042,8 @@ FILE: ../../../third_party/skia/gm/fpcoordinateoverride.cpp FILE: ../../../third_party/skia/gm/inverseclip.cpp FILE: ../../../third_party/skia/gm/labyrinth.cpp FILE: ../../../third_party/skia/gm/preservefillrule.cpp +FILE: ../../../third_party/skia/gm/sample_matrix_constant.cpp +FILE: ../../../third_party/skia/gm/sample_matrix_variable.cpp FILE: ../../../third_party/skia/gm/tilemodes_alpha.cpp FILE: ../../../third_party/skia/include/core/SkPathTypes.h FILE: ../../../third_party/skia/modules/skplaintexteditor/app/editor_application.cpp @@ -5627,6 +5638,8 @@ FILE: ../../../third_party/skia/src/core/SkColorFilterPriv.h FILE: ../../../third_party/skia/src/core/SkCompressedDataUtils.cpp FILE: ../../../third_party/skia/src/core/SkCompressedDataUtils.h FILE: ../../../third_party/skia/src/core/SkM44.cpp +FILE: ../../../third_party/skia/src/core/SkMarkerStack.cpp +FILE: ../../../third_party/skia/src/core/SkMarkerStack.h FILE: ../../../third_party/skia/src/core/SkVerticesPriv.h FILE: ../../../third_party/skia/src/gpu/GrDynamicAtlas.cpp FILE: ../../../third_party/skia/src/gpu/GrDynamicAtlas.h diff --git a/ci/licenses_golden/licenses_third_party b/ci/licenses_golden/licenses_third_party index 18cdd40a7c8e2..b21950f8c8eb0 100644 --- a/ci/licenses_golden/licenses_third_party +++ b/ci/licenses_golden/licenses_third_party @@ -1,4 +1,4 @@ -Signature: cdaa56a5f77c8a504af3e424af40fb9f +Signature: a15387d361603f161618092ca43df1e2 UNUSED LICENSES: @@ -68,10 +68,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== ==================================================================================================== -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/dart_internal/LICENSE +ORIGIN: ../../../third_party/pkg/when/LICENSE TYPE: LicenseType.bsd ---------------------------------------------------------------------------------------------------- -Copyright 2017, the Dart project authors. All rights reserved. +Copyright 2015, the Dart project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -543,7 +543,6 @@ POSSIBILITY OF SUCH DAMAGE. LIBRARY: angle LIBRARY: boringssl LIBRARY: khronos -LIBRARY: observatory_pub_packages LIBRARY: vulkan LIBRARY: wuffs ORIGIN: ../../../flutter/third_party/txt/LICENSE @@ -617,54 +616,6 @@ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_ FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_4096_sha512_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/rsa_signature_test.json FILE: ../../../third_party/boringssl/src/third_party/wycheproof_testvectors/x25519_test.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/async.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/cache.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/check.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/collection.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/core.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/io.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/iterables.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/mirrors.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/pattern.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/collect.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/concat.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/countdown_timer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/enumerate.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/future_stream.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/iteration.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/metronome.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/stream_buffer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/async/stream_router.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/cache/cache.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/cache/map_cache.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/bimap.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/delegates/iterable.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/delegates/list.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/delegates/map.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/delegates/queue.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/delegates/set.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/lru_map.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/multimap.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/collection/treeset.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/core/hash.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/core/optional.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/concat.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/count.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/cycle.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/enumerate.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/generating_iterable.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/infinite_iterable.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/merge.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/min_max.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/partition.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/range.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/iterables/zip.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/pattern/glob.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/time/clock.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/time/duration_unit_constants.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/src/time/util.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/strings.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/quiver/lib/time.dart FILE: ../../../third_party/khronos/GLES2/gl2platform.h FILE: ../../../third_party/khronos/GLES3/gl3platform.h FILE: ../../../third_party/vulkan/include/vulkan/vk_platform.h @@ -20651,16 +20602,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== ==================================================================================================== -LIBRARY: observatory_pub_packages LIBRARY: pkg -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/comparators.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/LICENSE +ORIGIN: ../../../third_party/pkg/when/lib/when.dart + ../../../third_party/pkg/when/LICENSE TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/comparators.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/unparsed_frame.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/expected_function.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/group_context.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/example/example.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/example/example.html FILE: ../../../third_party/pkg/when/lib/when.dart ---------------------------------------------------------------------------------------------------- Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file @@ -20693,1008 +20637,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================================================== -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/browser/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/.test_config -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/.test_config -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/.test_config -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/.analysis_options ----------------------------------------------------------------------------------------------------- -Copyright 2014, the Dart project authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/browser/lib/dart.js + ../../../third_party/dart/third_party/observatory_pub_packages/packages/browser/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/browser/lib/dart.js -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/browser/lib/interop.js -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/algorithms.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/collection.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/equality.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/iterable_zip.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/algorithms.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/equality.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/iterable_zip.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/unmodifiable_wrappers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/wrappers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/wrappers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/pretty_print.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/context.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/parsed_path.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/path_exception.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/style.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/style/posix.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/style/url.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/style/windows.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/chain.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/frame.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/lazy_trace.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/stack_zone_specification.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/trace.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/utils.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/vm_trace.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/stack_trace.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/compact_vm_config.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/html_config.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/html_enhanced_config.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/html_individual_config.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/configuration.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/pretty_print.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/simple_configuration.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/test_case.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/utils.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/unittest.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/vm_config.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/.analysis_options -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/charted-demo-screenshot.png -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/components/demo_chartstate.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/components/demo_chartstate.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/components/demo_custom_axis.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/demo_charts.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/demo_interactive.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_bar_charts.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_bar_charts.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_cartesian_renderers.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_combo_charts.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_combo_charts.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_line_charts.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_line_charts.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_pie_charts.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_pie_charts.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_stacked_bar_charts.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_stacked_bar_charts.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/index.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/layout/treemap_demo.html -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/selection/transitions_demo.html ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014, Michael Bostock and Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from this - software without specific prior written permission - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY -OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/components/demo_custom_axis.dart -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/components/demo_custom_axis.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/demo_interactive.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/charts/renderers/demo_cartesian_renderers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/layout/treemap_demo.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/examples/selection/transitions_demo.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charted.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/behaviors/axis_label_tooltip.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/behaviors/chart_tooltip.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/behaviors/hovercard.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/behaviors/line_marker.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/behaviors/mouse_tracker.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/cartesian_renderers/bubble_chart_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/cartesian_renderers/cartesian_base_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/cartesian_renderers/line_chart_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/cartesian_renderers/stackedbar_chart_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/cartesian_renderers/stackedline_chart_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_area.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_config.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_data.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_events.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_legend.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_series.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_state.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/chart_theme.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/charts.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/data_transformers/aggregation.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/data_transformers/aggregation_item.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/data_transformers/aggregation_transformer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/data_transformers/filter_transformer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/data_transformers/transpose_transformer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/layout_renderers/layout_base_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/layout_renderers/pie_chart_renderer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/cartesian_area_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/chart_axis_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/chart_config_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/chart_data_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/chart_events_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/chart_legend_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/chart_series_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/chart_state_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/src/layout_area_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/charts/themes/quantum_theme.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/interpolators.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/interpolators/easing.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/interpolators/interpolators.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/scales.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/scales/linear_scale.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/scales/log_scale.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/scales/ordinal_scale.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/scales/time_scale.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/text_metrics.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/text_metrics/segmentation.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/text_metrics/segmentation_data.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/text_metrics/segmentation_utils.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/time_interval.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/timer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/bidi_formatter.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/color.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/disposer.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/lists.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/math.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/namespace.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/object_factory.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/core/utils/rect.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/layout/layout.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/layout/src/hierarchy_layout.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/layout/src/pie_layout.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/layout/src/treemap_layout.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/locale/format.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/locale/format/number_format.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/locale/format/time_format.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/locale/languages/en_us.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/locale/locale.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/selection/selection.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/selection/selection_scope.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/selection/src/selection_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/selection/src/transition_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/selection/transition.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/svg/axis.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/svg/shapes.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/svg/shapes/arc.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/svg/shapes/area.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/svg/shapes/line.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/lib/svg/shapes/rect.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/tool/build_unicode_segmentation_data.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/charted/tool/hop_runner.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2013, Google Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -* Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/.test_config ----------------------------------------------------------------------------------------------------- -Copyright 2015, the Dart project authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/canonicalized_map.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/priority_queue.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/canonicalized_map.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/priority_queue.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/queue_list.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/utils.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/.status -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_time_patterns.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/logging/.status -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/mirror_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/util.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/benchmark/benchmark.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/characters.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/internal_style.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/utils.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/.status -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/internal_test_case.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/prints_matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/throws_matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/throws_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/util.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/test_environment.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/example/ga.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/lib/src/usage_impl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/lib/src/usage_impl_html.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/lib/src/usage_impl_io.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/lib/usage.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/lib/usage_html.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/lib/usage_io.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/lib/uuid/uuid.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/usage/tool/grind.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/combined_wrappers/combined_iterable.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/combined_wrappers/combined_iterable.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/combined_wrappers/combined_list.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/combined_wrappers/combined_map.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/dart_internal/lib/extract_type_arguments.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/meta/lib/dart2js.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/stack_trace/lib/src/lazy_chain.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/empty_unmodifiable_set.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/empty_unmodifiable_set.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/equality_map.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/equality_set.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/functions.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/union_set.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/collection/lib/src/union_set_controller.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl/compact_number_format.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/plural_rules.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/order_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/meta/lib/meta.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/observable.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/differs.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/differs/list_differ.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/differs/map_differ.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/internal.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/observable.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/observable_list.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/observable_map.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/records.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/records/list_change_record.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/records/map_change_record.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/records/property_change_record.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/to_observable.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/af.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/am.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ar.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ar_DZ.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/az.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/be.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/bg.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/bn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/br.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/bs.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ca.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/chr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/cs.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/cy.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/da.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/de.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/de_AT.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/de_CH.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/el.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_AU.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_CA.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_GB.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_IE.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_IN.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_ISO.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_MY.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_SG.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_US.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/en_ZA.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/es.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/es_419.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/es_ES.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/es_MX.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/es_US.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/et.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/eu.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/fa.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/fi.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/fil.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/fr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/fr_CA.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/fr_CH.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ga.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/gl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/gsw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/gu.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/haw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/he.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/hi.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/hr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/hu.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/hy.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/id.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/in.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/is.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/it.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/it_CH.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/iw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ja.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ka.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/kk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/km.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/kn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ko.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ky.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ln.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/lo.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/lt.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/lv.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/mk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ml.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/mn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/mo.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/mr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ms.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/mt.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/my.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/nb.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ne.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/nl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/no.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/no_NO.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/or.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/pa.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/pl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ps.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/pt.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/pt_BR.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/pt_PT.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ro.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ru.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sd.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sh.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/si.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sq.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sr_Latn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sv.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/sw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ta.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/te.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/th.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/tl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/tr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/uk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/ur.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/uz.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/vi.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/zh.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/zh_CN.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/zh_HK.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/zh_TW.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/patterns/zu.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/af.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/am.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ar.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ar_DZ.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/az.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/be.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/bg.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/bn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/br.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/bs.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ca.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/chr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/cs.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/cy.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/da.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/de.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/de_AT.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/de_CH.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/el.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_AU.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_CA.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_GB.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_IE.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_IN.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_ISO.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_MY.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_SG.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_US.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/en_ZA.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/es.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/es_419.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/es_ES.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/es_MX.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/es_US.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/et.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/eu.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/fa.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/fi.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/fil.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/fr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/fr_CA.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/fr_CH.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ga.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/gl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/gsw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/gu.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/haw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/he.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/hi.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/hr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/hu.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/hy.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/id.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/in.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/is.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/it.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/it_CH.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/iw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ja.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ka.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/kk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/km.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/kn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ko.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ky.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ln.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/lo.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/lt.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/lv.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/mk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ml.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/mn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/mr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ms.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/mt.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/my.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/nb.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ne.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/nl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/no.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/no_NO.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/or.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/pa.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/pl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ps.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/pt.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/pt_BR.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/pt_PT.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ro.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ru.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/si.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/sk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/sl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/sq.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/sr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/sr_Latn.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/sv.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/sw.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ta.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/te.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/th.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/tl.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/tr.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/uk.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/ur.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/uz.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/vi.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/zh.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/zh_CN.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/zh_HK.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/zh_TW.json -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/symbols/zu.json ----------------------------------------------------------------------------------------------------- -Copyright 2013, the Dart project authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_symbol_data_custom.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_symbol_data_custom.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2017, the Dart project authors. -Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_symbol_data_local.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_symbol_data_local.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/number_symbols_data.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2014, the Dart project authors. -Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/locale_list.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_symbol_data_file.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_symbol_data_http_request.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/date_symbols.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/intl.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/intl_browser.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/intl_standalone.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/message_lookup_by_library.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/number_symbols.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/data/dates/locale_list.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/date_format_internal.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/file_data_reader.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/http_request_data_reader.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl/bidi_formatter.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl/bidi_utils.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl/date_format.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl/date_format_field.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl/date_format_helpers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl/number_format.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/intl_helpers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/lib/src/lazy_locale_data.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/intl/tool/generate_locale_data_files.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/logging/lib/logging.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/core_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/custom_matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/description.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/error_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/interfaces.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/iterable_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/map_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/numeric_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/operator_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/string_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/path.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/core_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/description.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/error_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/expect.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/future_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/interfaces.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/iterable_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/map_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/numeric_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/operator_matchers.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/src/matcher/string_matchers.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/equals_matcher.dart + ../../../third_party/dart/third_party/observatory_pub_packages/packages/browser/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/equals_matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/feature_matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/having_matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/matcher/lib/src/type_matcher.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/path_map.dart -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/path/lib/src/path_set.dart ----------------------------------------------------------------------------------------------------- -Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/meta/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/observable/lib/src/change_notifier.dart ----------------------------------------------------------------------------------------------------- -Copyright 2016, the Dart project authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - -==================================================================================================== -LIBRARY: observatory_pub_packages -ORIGIN: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/coverage_controller.js + ../../../third_party/dart/third_party/observatory_pub_packages/packages/browser/LICENSE -TYPE: LicenseType.bsd -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/coverage_controller.js -FILE: ../../../third_party/dart/third_party/observatory_pub_packages/packages/unittest/lib/test_controller.js ----------------------------------------------------------------------------------------------------- -Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file -for details. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -==================================================================================================== - ==================================================================================================== LIBRARY: rapidjson ORIGIN: ../../../third_party/rapidjson/LICENSE @@ -23754,4 +22696,4 @@ freely, subject to the following restrictions: misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ==================================================================================================== -Total license count: 370 +Total license count: 355 diff --git a/flow/layers/image_filter_layer.cc b/flow/layers/image_filter_layer.cc index 212c396b6efbf..4df19eb66416b 100644 --- a/flow/layers/image_filter_layer.cc +++ b/flow/layers/image_filter_layer.cc @@ -31,9 +31,6 @@ void ImageFilterLayer::Preroll(PrerollContext* context, if (!context->has_platform_view && context->raster_cache && SkRect::Intersects(context->cull_rect, paint_bounds())) { SkMatrix ctm = matrix; -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - ctm = RasterCache::GetIntegralTransCTM(ctm); -#endif context->raster_cache->Prepare(context, this, ctm); } } @@ -42,12 +39,6 @@ void ImageFilterLayer::Paint(PaintContext& context) const { TRACE_EVENT0("flutter", "ImageFilterLayer::Paint"); FML_DCHECK(needs_painting()); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); - context.leaf_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( - context.leaf_nodes_canvas->getTotalMatrix())); -#endif - if (context.raster_cache) { const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); RasterCacheResult layer_cache = diff --git a/flow/layers/image_filter_layer_unittests.cc b/flow/layers/image_filter_layer_unittests.cc index f4f621a529a08..305ec01611068 100644 --- a/flow/layers/image_filter_layer_unittests.cc +++ b/flow/layers/image_filter_layer_unittests.cc @@ -60,14 +60,11 @@ TEST_F(ImageFilterLayerTest, EmptyFilter) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ - MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 2}}, + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + 1, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -96,14 +93,11 @@ TEST_F(ImageFilterLayerTest, SimpleFilter) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ - MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 2}}, + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + 1, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -132,14 +126,11 @@ TEST_F(ImageFilterLayerTest, SimpleFilterBounds) { layer->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ - MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{child_bounds, filter_paint, - nullptr, 2}}, + 0, MockCanvas::SaveLayerData{child_bounds, filter_paint, + nullptr, 1}}, MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path, child_paint}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, + 1, MockCanvas::DrawPathData{child_path, child_paint}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); } @@ -177,19 +168,16 @@ TEST_F(ImageFilterLayerTest, MultipleChildren) { SkPaint filter_paint; filter_paint.setImageFilter(layer_filter); layer->Paint(paint_context()); - EXPECT_EQ(mock_canvas().draw_calls(), - std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, - MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{children_bounds, filter_paint, - nullptr, 2}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); + EXPECT_EQ( + mock_canvas().draw_calls(), + std::vector({MockCanvas::DrawCall{ + 0, MockCanvas::SaveLayerData{children_bounds, + filter_paint, nullptr, 1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::DrawPathData{child_path2, child_paint2}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}})); } TEST_F(ImageFilterLayerTest, Nested) { @@ -237,22 +225,16 @@ TEST_F(ImageFilterLayerTest, Nested) { layer1->Paint(paint_context()); EXPECT_EQ(mock_canvas().draw_calls(), std::vector({ - MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, MockCanvas::SetMatrixData{SkMatrix()}}, MockCanvas::DrawCall{ - 1, MockCanvas::SaveLayerData{children_bounds, filter_paint1, - nullptr, 2}}, + 0, MockCanvas::SaveLayerData{children_bounds, filter_paint1, + nullptr, 1}}, MockCanvas::DrawCall{ - 2, MockCanvas::DrawPathData{child_path1, child_paint1}}, - MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, - MockCanvas::DrawCall{3, MockCanvas::SetMatrixData{SkMatrix()}}, + 1, MockCanvas::DrawPathData{child_path1, child_paint1}}, MockCanvas::DrawCall{ - 3, MockCanvas::SaveLayerData{child_path2.getBounds(), - filter_paint2, nullptr, 4}}, + 1, MockCanvas::SaveLayerData{child_path2.getBounds(), + filter_paint2, nullptr, 2}}, MockCanvas::DrawCall{ - 4, MockCanvas::DrawPathData{child_path2, child_paint2}}, - MockCanvas::DrawCall{4, MockCanvas::RestoreData{3}}, - MockCanvas::DrawCall{3, MockCanvas::RestoreData{2}}, + 2, MockCanvas::DrawPathData{child_path2, child_paint2}}, MockCanvas::DrawCall{2, MockCanvas::RestoreData{1}}, MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}, })); diff --git a/flow/layers/opacity_layer.cc b/flow/layers/opacity_layer.cc index 7899c31784b89..51fcc6c507fcb 100644 --- a/flow/layers/opacity_layer.cc +++ b/flow/layers/opacity_layer.cc @@ -51,9 +51,6 @@ void OpacityLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { if (!context->has_platform_view && context->raster_cache && SkRect::Intersects(context->cull_rect, paint_bounds())) { SkMatrix ctm = child_matrix; -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - ctm = RasterCache::GetIntegralTransCTM(ctm); -#endif context->raster_cache->Prepare(context, container, ctm); } } @@ -69,11 +66,6 @@ void OpacityLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(context.internal_nodes_canvas, true); context.internal_nodes_canvas->translate(offset_.fX, offset_.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - context.internal_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( - context.leaf_nodes_canvas->getTotalMatrix())); -#endif - if (context.raster_cache) { ContainerLayer* container = GetChildContainer(); const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); @@ -87,8 +79,7 @@ void OpacityLayer::Paint(PaintContext& context) const { // Skia may clip the content with saveLayerBounds (although it's not a // guaranteed clip). So we have to provide a big enough saveLayerBounds. To do // so, we first remove the offset from paint bounds since it's already in the - // matrix. Then we round out the bounds because of our - // RasterCache::GetIntegralTransCTM optimization. + // matrix. Then we round out the bounds. // // Note that the following lines are only accessible when the raster cache is // not available (e.g., when we're using the software backend in golden diff --git a/flow/layers/opacity_layer_unittests.cc b/flow/layers/opacity_layer_unittests.cc index f7cc77ffe2ddd..ae08109fa25cf 100644 --- a/flow/layers/opacity_layer_unittests.cc +++ b/flow/layers/opacity_layer_unittests.cc @@ -58,10 +58,6 @@ TEST_F(OpacityLayerTest, FullyOpaque) { const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); const SkMatrix layer_transform = SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer_transform)); -#endif const SkPaint child_paint = SkPaint(SkColors::kGreen); const SkRect expected_layer_bounds = layer_transform.mapRect(child_path.getBounds()); @@ -86,10 +82,6 @@ TEST_F(OpacityLayerTest, FullyOpaque) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, -#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, 2}}, @@ -107,10 +99,6 @@ TEST_F(OpacityLayerTest, FullyTransparent) { const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); const SkMatrix layer_transform = SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer_transform)); -#endif const SkPaint child_paint = SkPaint(SkColors::kGreen); const SkRect expected_layer_bounds = layer_transform.mapRect(child_path.getBounds()); @@ -133,10 +121,6 @@ TEST_F(OpacityLayerTest, FullyTransparent) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, -#endif MockCanvas::DrawCall{1, MockCanvas::SaveData{2}}, MockCanvas::DrawCall{ 2, MockCanvas::ClipRectData{kEmptyRect, SkClipOp::kIntersect, @@ -155,10 +139,6 @@ TEST_F(OpacityLayerTest, HalfTransparent) { const SkMatrix initial_transform = SkMatrix::MakeTrans(0.5f, 0.5f); const SkMatrix layer_transform = SkMatrix::MakeTrans(layer_offset.fX, layer_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer_transform)); -#endif const SkPaint child_paint = SkPaint(SkColors::kGreen); const SkRect expected_layer_bounds = layer_transform.mapRect(child_path.getBounds()); @@ -185,10 +165,6 @@ TEST_F(OpacityLayerTest, HalfTransparent) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer_transform}}, -#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity_bounds, opacity_paint, nullptr, 2}}, @@ -211,13 +187,6 @@ TEST_F(OpacityLayerTest, Nested) { SkMatrix::MakeTrans(layer1_offset.fX, layer1_offset.fY); const SkMatrix layer2_transform = SkMatrix::MakeTrans(layer2_offset.fX, layer2_offset.fY); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - const SkMatrix integral_layer1_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(initial_transform, layer1_transform)); - const SkMatrix integral_layer2_transform = RasterCache::GetIntegralTransCTM( - SkMatrix::Concat(SkMatrix::Concat(initial_transform, layer1_transform), - layer2_transform)); -#endif const SkPaint child1_paint = SkPaint(SkColors::kRed); const SkPaint child2_paint = SkPaint(SkColors::kBlue); const SkPaint child3_paint = SkPaint(SkColors::kGreen); @@ -278,10 +247,6 @@ TEST_F(OpacityLayerTest, Nested) { auto expected_draw_calls = std::vector( {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, MockCanvas::DrawCall{1, MockCanvas::ConcatMatrixData{layer1_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{integral_layer1_transform}}, -#endif MockCanvas::DrawCall{ 1, MockCanvas::SaveLayerData{opacity1_bounds, opacity1_paint, nullptr, 2}}, @@ -289,10 +254,6 @@ TEST_F(OpacityLayerTest, Nested) { 2, MockCanvas::DrawPathData{child1_path, child1_paint}}, MockCanvas::DrawCall{2, MockCanvas::SaveData{3}}, MockCanvas::DrawCall{3, MockCanvas::ConcatMatrixData{layer2_transform}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 3, MockCanvas::SetMatrixData{integral_layer2_transform}}, -#endif MockCanvas::DrawCall{ 3, MockCanvas::SaveLayerData{opacity2_bounds, opacity2_paint, nullptr, 4}}, diff --git a/flow/layers/picture_layer.cc b/flow/layers/picture_layer.cc index 08c09cc9e833b..5a615168b1de5 100644 --- a/flow/layers/picture_layer.cc +++ b/flow/layers/picture_layer.cc @@ -26,9 +26,6 @@ void PictureLayer::Preroll(PrerollContext* context, const SkMatrix& matrix) { SkMatrix ctm = matrix; ctm.postTranslate(offset_.x(), offset_.y()); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - ctm = RasterCache::GetIntegralTransCTM(ctm); -#endif cache->Prepare(context->gr_context, sk_picture, ctm, context->dst_color_space, is_complex_, will_change_); } @@ -44,10 +41,6 @@ void PictureLayer::Paint(PaintContext& context) const { SkAutoCanvasRestore save(context.leaf_nodes_canvas, true); context.leaf_nodes_canvas->translate(offset_.x(), offset_.y()); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - context.leaf_nodes_canvas->setMatrix(RasterCache::GetIntegralTransCTM( - context.leaf_nodes_canvas->getTotalMatrix())); -#endif if (context.raster_cache) { const SkMatrix& ctm = context.leaf_nodes_canvas->getTotalMatrix(); diff --git a/flow/layers/picture_layer_unittests.cc b/flow/layers/picture_layer_unittests.cc index 4f565cf500ecc..0cdb2f94f4542 100644 --- a/flow/layers/picture_layer_unittests.cc +++ b/flow/layers/picture_layer_unittests.cc @@ -11,10 +11,6 @@ #include "flutter/testing/mock_canvas.h" #include "third_party/skia/include/core/SkPicture.h" -#ifndef SUPPORT_FRACTIONAL_TRANSLATION -#include "flutter/flow/raster_cache.h" -#endif - namespace flutter { namespace testing { @@ -85,16 +81,11 @@ TEST_F(PictureLayerTest, SimplePicture) { EXPECT_FALSE(layer->needs_system_composite()); layer->Paint(paint_context()); - auto expected_draw_calls = std::vector( - {MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, - MockCanvas::DrawCall{1, - MockCanvas::ConcatMatrixData{layer_offset_matrix}}, -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - MockCanvas::DrawCall{ - 1, MockCanvas::SetMatrixData{RasterCache::GetIntegralTransCTM( - layer_offset_matrix)}}, -#endif - MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); + auto expected_draw_calls = + std::vector({MockCanvas::DrawCall{0, MockCanvas::SaveData{1}}, + MockCanvas::DrawCall{ + 1, MockCanvas::ConcatMatrixData{layer_offset_matrix}}, + MockCanvas::DrawCall{1, MockCanvas::RestoreData{0}}}); EXPECT_EQ(mock_canvas().draw_calls(), expected_draw_calls); } diff --git a/flow/matrix_decomposition.cc b/flow/matrix_decomposition.cc index 1935393906513..c9bb1ff475da2 100644 --- a/flow/matrix_decomposition.cc +++ b/flow/matrix_decomposition.cc @@ -6,140 +6,114 @@ namespace flutter { -static inline SkVector3 SkVector3Combine(const SkVector3& a, - float a_scale, - const SkVector3& b, - float b_scale) { - return { - a_scale * a.fX + b_scale * b.fX, // - a_scale * a.fY + b_scale * b.fY, // - a_scale * a.fZ + b_scale * b.fZ, // - }; -} - -static inline SkVector3 SkVector3Cross(const SkVector3& a, const SkVector3& b) { - return { - (a.fY * b.fZ) - (a.fZ * b.fY), // - (a.fZ * b.fX) - (a.fX * b.fZ), // - (a.fX * b.fY) - (a.fY * b.fX) // - }; +static inline SkV3 SkV3Combine(const SkV3& a, + float a_scale, + const SkV3& b, + float b_scale) { + return (a * a_scale) + (b * b_scale); } MatrixDecomposition::MatrixDecomposition(const SkMatrix& matrix) - : MatrixDecomposition(SkMatrix44{matrix}) {} + : MatrixDecomposition(SkM44{matrix}) {} // Use custom normalize to avoid skia precision loss/normalize() privatization. -static inline void SkVector3Normalize(SkVector3& v) { - double mag = sqrt(v.fX * v.fX + v.fY * v.fY + v.fZ * v.fZ); +static inline void SkV3Normalize(SkV3& v) { + double mag = sqrt(v.x * v.x + v.y * v.y + v.z * v.z); double scale = 1.0 / mag; - v.fX *= scale; - v.fY *= scale; - v.fZ *= scale; + v.x *= scale; + v.y *= scale; + v.z *= scale; } -MatrixDecomposition::MatrixDecomposition(SkMatrix44 matrix) : valid_(false) { - if (matrix.get(3, 3) == 0) { +MatrixDecomposition::MatrixDecomposition(SkM44 matrix) : valid_(false) { + if (matrix.rc(3, 3) == 0) { return; } for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { - matrix.set(j, i, matrix.get(j, i) / matrix.get(3, 3)); + matrix.setRC(j, i, matrix.rc(j, i) / matrix.rc(3, 3)); } } - SkMatrix44 perpective_matrix = matrix; + SkM44 perpective_matrix = matrix; for (int i = 0; i < 3; i++) { - perpective_matrix.set(3, i, 0.0); + perpective_matrix.setRC(3, i, 0.0); } - perpective_matrix.set(3, 3, 1.0); + perpective_matrix.setRC(3, 3, 1.0); - if (perpective_matrix.determinant() == 0.0) { + SkM44 inverted(SkM44::Uninitialized_Constructor::kUninitialized_Constructor); + if (!perpective_matrix.invert(&inverted)) { return; } - if (matrix.get(3, 0) != 0.0 || matrix.get(3, 1) != 0.0 || - matrix.get(3, 2) != 0.0) { - const SkVector4 right_hand_side(matrix.get(3, 0), matrix.get(3, 1), - matrix.get(3, 2), matrix.get(3, 3)); - - SkMatrix44 inverted_transposed( - SkMatrix44::Uninitialized_Constructor::kUninitialized_Constructor); - if (!perpective_matrix.invert(&inverted_transposed)) { - return; - } - inverted_transposed.transpose(); + if (matrix.rc(3, 0) != 0.0 || matrix.rc(3, 1) != 0.0 || + matrix.rc(3, 2) != 0.0) { + const SkV4 right_hand_side = matrix.row(3); - perspective_ = inverted_transposed * right_hand_side; + perspective_ = inverted.transpose() * right_hand_side; - matrix.set(3, 0, 0); - matrix.set(3, 1, 0); - matrix.set(3, 2, 0); - matrix.set(3, 3, 1); + matrix.setRow(3, {0, 0, 0, 1}); } - translation_ = {matrix.get(0, 3), matrix.get(1, 3), matrix.get(2, 3)}; + translation_ = {matrix.rc(0, 3), matrix.rc(1, 3), matrix.rc(2, 3)}; - matrix.set(0, 3, 0.0); - matrix.set(1, 3, 0.0); - matrix.set(2, 3, 0.0); + matrix.setRC(0, 3, 0.0); + matrix.setRC(1, 3, 0.0); + matrix.setRC(2, 3, 0.0); - SkVector3 row[3]; + SkV3 row[3]; for (int i = 0; i < 3; i++) { - row[i].set(matrix.get(0, i), matrix.get(1, i), matrix.get(2, i)); + row[i] = {matrix.rc(0, i), matrix.rc(1, i), matrix.rc(2, i)}; } - scale_.fX = row[0].length(); + scale_.x = row[0].length(); - SkVector3Normalize(row[0]); + SkV3Normalize(row[0]); - shear_.fX = row[0].dot(row[1]); - row[1] = SkVector3Combine(row[1], 1.0, row[0], -shear_.fX); + shear_.x = row[0].dot(row[1]); + row[1] = SkV3Combine(row[1], 1.0, row[0], -shear_.x); - scale_.fY = row[1].length(); + scale_.y = row[1].length(); - SkVector3Normalize(row[1]); + SkV3Normalize(row[1]); - shear_.fX /= scale_.fY; + shear_.x /= scale_.y; - shear_.fY = row[0].dot(row[2]); - row[2] = SkVector3Combine(row[2], 1.0, row[0], -shear_.fY); - shear_.fZ = row[1].dot(row[2]); - row[2] = SkVector3Combine(row[2], 1.0, row[1], -shear_.fZ); + shear_.y = row[0].dot(row[2]); + row[2] = SkV3Combine(row[2], 1.0, row[0], -shear_.y); + shear_.z = row[1].dot(row[2]); + row[2] = SkV3Combine(row[2], 1.0, row[1], -shear_.z); - scale_.fZ = row[2].length(); + scale_.z = row[2].length(); - SkVector3Normalize(row[2]); + SkV3Normalize(row[2]); - shear_.fY /= scale_.fZ; - shear_.fZ /= scale_.fZ; + shear_.y /= scale_.z; + shear_.z /= scale_.z; - if (row[0].dot(SkVector3Cross(row[1], row[2])) < 0) { - scale_.fX *= -1; - scale_.fY *= -1; - scale_.fZ *= -1; + if (row[0].dot(row[1].cross(row[2])) < 0) { + scale_ *= -1; for (int i = 0; i < 3; i++) { - row[i].fX *= -1; - row[i].fY *= -1; - row[i].fZ *= -1; + row[i] *= -1; } } - rotation_.set(0.5 * sqrt(fmax(1.0 + row[0].fX - row[1].fY - row[2].fZ, 0.0)), - 0.5 * sqrt(fmax(1.0 - row[0].fX + row[1].fY - row[2].fZ, 0.0)), - 0.5 * sqrt(fmax(1.0 - row[0].fX - row[1].fY + row[2].fZ, 0.0)), - 0.5 * sqrt(fmax(1.0 + row[0].fX + row[1].fY + row[2].fZ, 0.0))); + rotation_.x = 0.5 * sqrt(fmax(1.0 + row[0].x - row[1].y - row[2].z, 0.0)); + rotation_.y = 0.5 * sqrt(fmax(1.0 - row[0].x + row[1].y - row[2].z, 0.0)); + rotation_.z = 0.5 * sqrt(fmax(1.0 - row[0].x - row[1].y + row[2].z, 0.0)); + rotation_.w = 0.5 * sqrt(fmax(1.0 + row[0].x + row[1].y + row[2].z, 0.0)); - if (row[2].fY > row[1].fZ) { - rotation_.fData[0] = -rotation_.fData[0]; + if (row[2].y > row[1].z) { + rotation_.x = -rotation_.x; } - if (row[0].fZ > row[2].fX) { - rotation_.fData[1] = -rotation_.fData[1]; + if (row[0].z > row[2].x) { + rotation_.y = -rotation_.y; } - if (row[1].fX > row[0].fY) { - rotation_.fData[2] = -rotation_.fData[2]; + if (row[1].x > row[0].y) { + rotation_.z = -rotation_.z; } valid_ = true; diff --git a/flow/matrix_decomposition.h b/flow/matrix_decomposition.h index 81794c2fe589f..ea0af37ae9b50 100644 --- a/flow/matrix_decomposition.h +++ b/flow/matrix_decomposition.h @@ -6,9 +6,8 @@ #define FLUTTER_FLOW_MATRIX_DECOMPOSITION_H_ #include "flutter/fml/macros.h" +#include "third_party/skia/include/core/SkM44.h" #include "third_party/skia/include/core/SkMatrix.h" -#include "third_party/skia/include/core/SkMatrix44.h" -#include "third_party/skia/include/core/SkPoint3.h" namespace flutter { @@ -19,29 +18,29 @@ class MatrixDecomposition { public: MatrixDecomposition(const SkMatrix& matrix); - MatrixDecomposition(SkMatrix44 matrix); + MatrixDecomposition(SkM44 matrix); ~MatrixDecomposition(); bool IsValid() const; - const SkVector3& translation() const { return translation_; } + const SkV3& translation() const { return translation_; } - const SkVector3& scale() const { return scale_; } + const SkV3& scale() const { return scale_; } - const SkVector3& shear() const { return shear_; } + const SkV3& shear() const { return shear_; } - const SkVector4& perspective() const { return perspective_; } + const SkV4& perspective() const { return perspective_; } - const SkVector4& rotation() const { return rotation_; } + const SkV4& rotation() const { return rotation_; } private: bool valid_; - SkVector3 translation_; - SkVector3 scale_; - SkVector3 shear_; - SkVector4 perspective_; - SkVector4 rotation_; + SkV3 translation_; + SkV3 scale_; + SkV3 shear_; + SkV4 perspective_; + SkV4 rotation_; FML_DISALLOW_COPY_AND_ASSIGN(MatrixDecomposition); }; diff --git a/flow/matrix_decomposition_unittests.cc b/flow/matrix_decomposition_unittests.cc index 8aa511e4a0a97..fb39cf0f94c85 100644 --- a/flow/matrix_decomposition_unittests.cc +++ b/flow/matrix_decomposition_unittests.cc @@ -16,24 +16,24 @@ namespace flutter { namespace testing { TEST(MatrixDecomposition, Rotation) { - SkMatrix44 matrix = SkMatrix44::I(); + SkM44 matrix; const auto angle = M_PI_4; - matrix.setRotateAbout(0.0, 0.0, 1.0, angle); + matrix.setRotate({0.0, 0.0, 1.0}, angle); flutter::MatrixDecomposition decomposition(matrix); ASSERT_TRUE(decomposition.IsValid()); const auto sine = sin(angle * 0.5); - ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[0]); - ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[1]); - ASSERT_FLOAT_EQ(sine, decomposition.rotation().fData[2]); - ASSERT_FLOAT_EQ(cos(angle * 0.5), decomposition.rotation().fData[3]); + ASSERT_FLOAT_EQ(0, decomposition.rotation().x); + ASSERT_FLOAT_EQ(0, decomposition.rotation().y); + ASSERT_FLOAT_EQ(sine, decomposition.rotation().z); + ASSERT_FLOAT_EQ(cos(angle * 0.5), decomposition.rotation().w); } TEST(MatrixDecomposition, Scale) { - SkMatrix44 matrix = SkMatrix44::I(); + SkM44 matrix; const auto scale = 5.0; matrix.setScale(scale + 0, scale + 1, scale + 2); @@ -41,13 +41,13 @@ TEST(MatrixDecomposition, Scale) { flutter::MatrixDecomposition decomposition(matrix); ASSERT_TRUE(decomposition.IsValid()); - ASSERT_FLOAT_EQ(scale + 0, decomposition.scale().fX); - ASSERT_FLOAT_EQ(scale + 1, decomposition.scale().fY); - ASSERT_FLOAT_EQ(scale + 2, decomposition.scale().fZ); + ASSERT_FLOAT_EQ(scale + 0, decomposition.scale().x); + ASSERT_FLOAT_EQ(scale + 1, decomposition.scale().y); + ASSERT_FLOAT_EQ(scale + 2, decomposition.scale().z); } TEST(MatrixDecomposition, Translate) { - SkMatrix44 matrix = SkMatrix44::I(); + SkM44 matrix; const auto translate = 125.0; matrix.setTranslate(translate + 0, translate + 1, translate + 2); @@ -55,9 +55,9 @@ TEST(MatrixDecomposition, Translate) { flutter::MatrixDecomposition decomposition(matrix); ASSERT_TRUE(decomposition.IsValid()); - ASSERT_FLOAT_EQ(translate + 0, decomposition.translation().fX); - ASSERT_FLOAT_EQ(translate + 1, decomposition.translation().fY); - ASSERT_FLOAT_EQ(translate + 2, decomposition.translation().fZ); + ASSERT_FLOAT_EQ(translate + 0, decomposition.translation().x); + ASSERT_FLOAT_EQ(translate + 1, decomposition.translation().y); + ASSERT_FLOAT_EQ(translate + 2, decomposition.translation().z); } TEST(MatrixDecomposition, Combination) { @@ -65,65 +65,65 @@ TEST(MatrixDecomposition, Combination) { const auto scale = 5; const auto translate = 125.0; - SkMatrix44 m1 = SkMatrix44::I(); - m1.setRotateAbout(0, 0, 1, rotation); + SkM44 m1; + m1.setRotate({0, 0, 1}, rotation); - SkMatrix44 m2 = SkMatrix44::I(); - m2.setScale(scale); + SkM44 m2; + m2.setScale(scale, scale, scale); - SkMatrix44 m3 = SkMatrix44::I(); + SkM44 m3; m3.setTranslate(translate, translate, translate); - SkMatrix44 combined = m3 * m2 * m1; + SkM44 combined = m3 * m2 * m1; flutter::MatrixDecomposition decomposition(combined); ASSERT_TRUE(decomposition.IsValid()); - ASSERT_FLOAT_EQ(translate, decomposition.translation().fX); - ASSERT_FLOAT_EQ(translate, decomposition.translation().fY); - ASSERT_FLOAT_EQ(translate, decomposition.translation().fZ); + ASSERT_FLOAT_EQ(translate, decomposition.translation().x); + ASSERT_FLOAT_EQ(translate, decomposition.translation().y); + ASSERT_FLOAT_EQ(translate, decomposition.translation().z); - ASSERT_FLOAT_EQ(scale, decomposition.scale().fX); - ASSERT_FLOAT_EQ(scale, decomposition.scale().fY); - ASSERT_FLOAT_EQ(scale, decomposition.scale().fZ); + ASSERT_FLOAT_EQ(scale, decomposition.scale().x); + ASSERT_FLOAT_EQ(scale, decomposition.scale().y); + ASSERT_FLOAT_EQ(scale, decomposition.scale().z); const auto sine = sin(rotation * 0.5); - ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[0]); - ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[1]); - ASSERT_FLOAT_EQ(sine, decomposition.rotation().fData[2]); - ASSERT_FLOAT_EQ(cos(rotation * 0.5), decomposition.rotation().fData[3]); + ASSERT_FLOAT_EQ(0, decomposition.rotation().x); + ASSERT_FLOAT_EQ(0, decomposition.rotation().y); + ASSERT_FLOAT_EQ(sine, decomposition.rotation().z); + ASSERT_FLOAT_EQ(cos(rotation * 0.5), decomposition.rotation().w); } TEST(MatrixDecomposition, ScaleFloatError) { constexpr float scale_increment = 0.00001f; for (float scale = 0.0001f; scale < 2.0f; scale += scale_increment) { - SkMatrix44 matrix = SkMatrix44::I(); + SkM44 matrix; matrix.setScale(scale, scale, 1.0f); flutter::MatrixDecomposition decomposition3(matrix); ASSERT_TRUE(decomposition3.IsValid()); - ASSERT_FLOAT_EQ(scale, decomposition3.scale().fX); - ASSERT_FLOAT_EQ(scale, decomposition3.scale().fY); - ASSERT_FLOAT_EQ(1.f, decomposition3.scale().fZ); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[0]); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[1]); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[2]); + ASSERT_FLOAT_EQ(scale, decomposition3.scale().x); + ASSERT_FLOAT_EQ(scale, decomposition3.scale().y); + ASSERT_FLOAT_EQ(1.f, decomposition3.scale().z); + ASSERT_FLOAT_EQ(0, decomposition3.rotation().x); + ASSERT_FLOAT_EQ(0, decomposition3.rotation().y); + ASSERT_FLOAT_EQ(0, decomposition3.rotation().z); } - SkMatrix44 matrix = SkMatrix44::I(); + SkM44 matrix; const auto scale = 1.7734375f; matrix.setScale(scale, scale, 1.f); // Bug upper bound (empirical) const auto scale2 = 1.773437559603f; - SkMatrix44 matrix2 = SkMatrix44::I(); + SkM44 matrix2; matrix2.setScale(scale2, scale2, 1.f); // Bug lower bound (empirical) const auto scale3 = 1.7734374403954f; - SkMatrix44 matrix3 = SkMatrix44::I(); + SkM44 matrix3; matrix3.setScale(scale3, scale3, 1.f); flutter::MatrixDecomposition decomposition(matrix); @@ -135,26 +135,26 @@ TEST(MatrixDecomposition, ScaleFloatError) { flutter::MatrixDecomposition decomposition3(matrix3); ASSERT_TRUE(decomposition3.IsValid()); - ASSERT_FLOAT_EQ(scale, decomposition.scale().fX); - ASSERT_FLOAT_EQ(scale, decomposition.scale().fY); - ASSERT_FLOAT_EQ(1.f, decomposition.scale().fZ); - ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[0]); - ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[1]); - ASSERT_FLOAT_EQ(0, decomposition.rotation().fData[2]); - - ASSERT_FLOAT_EQ(scale2, decomposition2.scale().fX); - ASSERT_FLOAT_EQ(scale2, decomposition2.scale().fY); - ASSERT_FLOAT_EQ(1.f, decomposition2.scale().fZ); - ASSERT_FLOAT_EQ(0, decomposition2.rotation().fData[0]); - ASSERT_FLOAT_EQ(0, decomposition2.rotation().fData[1]); - ASSERT_FLOAT_EQ(0, decomposition2.rotation().fData[2]); - - ASSERT_FLOAT_EQ(scale3, decomposition3.scale().fX); - ASSERT_FLOAT_EQ(scale3, decomposition3.scale().fY); - ASSERT_FLOAT_EQ(1.f, decomposition3.scale().fZ); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[0]); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[1]); - ASSERT_FLOAT_EQ(0, decomposition3.rotation().fData[2]); + ASSERT_FLOAT_EQ(scale, decomposition.scale().x); + ASSERT_FLOAT_EQ(scale, decomposition.scale().y); + ASSERT_FLOAT_EQ(1.f, decomposition.scale().z); + ASSERT_FLOAT_EQ(0, decomposition.rotation().x); + ASSERT_FLOAT_EQ(0, decomposition.rotation().y); + ASSERT_FLOAT_EQ(0, decomposition.rotation().z); + + ASSERT_FLOAT_EQ(scale2, decomposition2.scale().x); + ASSERT_FLOAT_EQ(scale2, decomposition2.scale().y); + ASSERT_FLOAT_EQ(1.f, decomposition2.scale().z); + ASSERT_FLOAT_EQ(0, decomposition2.rotation().x); + ASSERT_FLOAT_EQ(0, decomposition2.rotation().y); + ASSERT_FLOAT_EQ(0, decomposition2.rotation().z); + + ASSERT_FLOAT_EQ(scale3, decomposition3.scale().x); + ASSERT_FLOAT_EQ(scale3, decomposition3.scale().y); + ASSERT_FLOAT_EQ(1.f, decomposition3.scale().z); + ASSERT_FLOAT_EQ(0, decomposition3.rotation().x); + ASSERT_FLOAT_EQ(0, decomposition3.rotation().y); + ASSERT_FLOAT_EQ(0, decomposition3.rotation().z); } } // namespace testing diff --git a/flow/raster_cache.h b/flow/raster_cache.h index b51382a5c8d2d..2cbe07db5c6ba 100644 --- a/flow/raster_cache.h +++ b/flow/raster_cache.h @@ -62,13 +62,6 @@ class RasterCache { return bounds; } - static SkMatrix GetIntegralTransCTM(const SkMatrix& ctm) { - SkMatrix result = ctm; - result[SkMatrix::kMTransX] = SkScalarRoundToScalar(ctm.getTranslateX()); - result[SkMatrix::kMTransY] = SkScalarRoundToScalar(ctm.getTranslateY()); - return result; - } - // Return true if the cache is generated. // // We may return false and not generate the cache if diff --git a/flow/raster_cache_key.h b/flow/raster_cache_key.h index 5eae4d3dccacb..f5fd1e0ad1edb 100644 --- a/flow/raster_cache_key.h +++ b/flow/raster_cache_key.h @@ -15,11 +15,8 @@ template class RasterCacheKey { public: RasterCacheKey(ID id, const SkMatrix& ctm) : id_(id), matrix_(ctm) { - matrix_[SkMatrix::kMTransX] = SkScalarFraction(ctm.getTranslateX()); - matrix_[SkMatrix::kMTransY] = SkScalarFraction(ctm.getTranslateY()); -#ifndef SUPPORT_FRACTIONAL_TRANSLATION - FML_DCHECK(matrix_.getTranslateX() == 0 && matrix_.getTranslateY() == 0); -#endif + matrix_[SkMatrix::kMTransX] = 0; + matrix_[SkMatrix::kMTransY] = 0; } ID id() const { return id_; } diff --git a/flow/scene_update_context.cc b/flow/scene_update_context.cc index 1dadfd7327f23..bca3debe05e70 100644 --- a/flow/scene_update_context.cc +++ b/flow/scene_update_context.cc @@ -250,22 +250,22 @@ SceneUpdateContext::Transform::Transform(SceneUpdateContext& context, if (decomposition.IsValid()) { // Don't allow clients to control the z dimension; we control that // instead to make sure layers appear in proper order. - entity_node().SetTranslation(decomposition.translation().x(), // - decomposition.translation().y(), // - 0.f // + entity_node().SetTranslation(decomposition.translation().x, // + decomposition.translation().y, // + 0.f // ); - entity_node().SetScale(decomposition.scale().x(), // - decomposition.scale().y(), // - 1.f // + entity_node().SetScale(decomposition.scale().x, // + decomposition.scale().y, // + 1.f // ); - context.top_scale_x_ *= decomposition.scale().x(); - context.top_scale_y_ *= decomposition.scale().y(); + context.top_scale_x_ *= decomposition.scale().x; + context.top_scale_y_ *= decomposition.scale().y; - entity_node().SetRotation(decomposition.rotation().fData[0], // - decomposition.rotation().fData[1], // - decomposition.rotation().fData[2], // - decomposition.rotation().fData[3] // + entity_node().SetRotation(decomposition.rotation().x, // + decomposition.rotation().y, // + decomposition.rotation().z, // + decomposition.rotation().w // ); } } diff --git a/lib/web_ui/lib/src/engine/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/bitmap_canvas.dart index e0ba89020de02..d9bd8fa25aef6 100644 --- a/lib/web_ui/lib/src/engine/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/bitmap_canvas.dart @@ -489,10 +489,11 @@ class BitmapCanvas extends EngineCanvas { @override void drawParagraph(EngineParagraph paragraph, ui.Offset offset) { assert(paragraph._isLaidOut); - html.CanvasRenderingContext2D ctx = _canvasPool.context; final ParagraphGeometricStyle style = paragraph._geometricStyle; if (paragraph._drawOnCanvas && _childOverdraw == false) { + // !Do not move this assignment above this if clause since, accessing + // context will generate extra tags. final List lines = paragraph._measurementResult.lines; final SurfacePaintData backgroundPaint = paragraph._background?.paintData; @@ -503,6 +504,7 @@ class BitmapCanvas extends EngineCanvas { } if (style != _cachedLastStyle) { + html.CanvasRenderingContext2D ctx = _canvasPool.context; ctx.font = style.cssFontString; _cachedLastStyle = style; } @@ -543,7 +545,7 @@ class BitmapCanvas extends EngineCanvas { /// Paints the [picture] into this canvas. void drawPicture(ui.Picture picture) { final EnginePicture enginePicture = picture; - enginePicture.recordingCanvas.apply(this); + enginePicture.recordingCanvas.apply(this, bounds); } /// Draws vertices on a gl context. diff --git a/lib/web_ui/lib/src/engine/picture.dart b/lib/web_ui/lib/src/engine/picture.dart index d88272cefc9ea..04010a28f27f4 100644 --- a/lib/web_ui/lib/src/engine/picture.dart +++ b/lib/web_ui/lib/src/engine/picture.dart @@ -32,6 +32,7 @@ class EnginePictureRecorder implements ui.PictureRecorder { return null; } _isRecording = false; + _canvas.endRecording(); return EnginePicture(_canvas, cullRect); } } @@ -46,8 +47,9 @@ class EnginePicture implements ui.Picture { @override Future toImage(int width, int height) async { - final BitmapCanvas canvas = BitmapCanvas(ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble())); - recordingCanvas.apply(canvas); + final ui.Rect imageRect = ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble()); + final BitmapCanvas canvas = BitmapCanvas(imageRect); + recordingCanvas.apply(canvas, imageRect); final String imageDataUrl = canvas.toDataUrl(); final html.ImageElement imageElement = html.ImageElement() ..src = imageDataUrl diff --git a/lib/web_ui/lib/src/engine/surface/picture.dart b/lib/web_ui/lib/src/engine/surface/picture.dart index c0a5f2198cc75..50d77b0560cc2 100644 --- a/lib/web_ui/lib/src/engine/surface/picture.dart +++ b/lib/web_ui/lib/src/engine/surface/picture.dart @@ -145,7 +145,7 @@ class PersistedHoudiniPicture extends PersistedPicture { _canvas = canvas; domRenderer.clearDom(rootElement); rootElement.append(_canvas.rootElement); - picture.recordingCanvas.apply(_canvas); + picture.recordingCanvas.apply(_canvas, _optimalLocalCullRect); canvas.commit(); } } @@ -231,7 +231,7 @@ class PersistedStandardPicture extends PersistedPicture { _canvas = DomCanvas(); domRenderer.clearDom(rootElement); rootElement.append(_canvas.rootElement); - picture.recordingCanvas.apply(_canvas); + picture.recordingCanvas.apply(_canvas, _optimalLocalCullRect); } void _applyBitmapPaint(EngineCanvas oldCanvas) { @@ -244,7 +244,7 @@ class PersistedStandardPicture extends PersistedPicture { oldCanvas.bounds = _optimalLocalCullRect; _canvas = oldCanvas; _canvas.clear(); - picture.recordingCanvas.apply(_canvas); + picture.recordingCanvas.apply(_canvas, _optimalLocalCullRect); } else { // We can't use the old canvas because the size has changed, so we put // it in a cache for later reuse. @@ -265,7 +265,7 @@ class PersistedStandardPicture extends PersistedPicture { domRenderer.clearDom(rootElement); rootElement.append(_canvas.rootElement); _canvas.clear(); - picture.recordingCanvas.apply(_canvas); + picture.recordingCanvas.apply(_canvas, _optimalLocalCullRect); }, )); } @@ -352,7 +352,7 @@ class PersistedStandardPicture extends PersistedPicture { /// to draw shapes and text. abstract class PersistedPicture extends PersistedLeafSurface { PersistedPicture(this.dx, this.dy, this.picture, this.hints) - : localPaintBounds = picture.recordingCanvas.computePaintBounds(); + : localPaintBounds = picture.recordingCanvas.pictureBounds; EngineCanvas _canvas; @@ -491,7 +491,7 @@ abstract class PersistedPicture extends PersistedLeafSurface { // The new cull rect contains area not covered by a previous rect. Perhaps // the clip is growing, moving around the picture, or both. In this case - // a part of the picture may not been painted. We will need to + // a part of the picture may not have been painted. We will need to // request a new canvas and paint the picture on it. However, this is also // a strong signal that the clip will continue growing as typically // Flutter uses animated transitions. So instead of allocating the canvas @@ -500,25 +500,19 @@ abstract class PersistedPicture extends PersistedLeafSurface { // will hit the above case where the new cull rect is fully contained // within the cull rect we compute now. - // If any of the borders moved. - // TODO(yjbanov): consider switching to Mouad's snap-to-10px strategy. It - // might be sufficient, if not more effective. - const double kPredictedGrowthFactor = 3.0; - final double leftwardTrend = kPredictedGrowthFactor * - math.max(oldOptimalLocalCullRect.left - _exactLocalCullRect.left, 0); - final double upwardTrend = kPredictedGrowthFactor * - math.max(oldOptimalLocalCullRect.top - _exactLocalCullRect.top, 0); - final double rightwardTrend = kPredictedGrowthFactor * - math.max(_exactLocalCullRect.right - oldOptimalLocalCullRect.right, 0); - final double bottomwardTrend = kPredictedGrowthFactor * - math.max( - _exactLocalCullRect.bottom - oldOptimalLocalCullRect.bottom, 0); + // Compute the delta, by which each of the side of the clip rect has "moved" + // since the last time we updated the cull rect. + final double leftwardDelta = oldOptimalLocalCullRect.left - _exactLocalCullRect.left; + final double upwardDelta = oldOptimalLocalCullRect.top - _exactLocalCullRect.top; + final double rightwardDelta = _exactLocalCullRect.right - oldOptimalLocalCullRect.right; + final double bottomwardDelta = _exactLocalCullRect.bottom - oldOptimalLocalCullRect.bottom; + // Compute the new optimal rect to paint into. final ui.Rect newLocalCullRect = ui.Rect.fromLTRB( - oldOptimalLocalCullRect.left - leftwardTrend, - oldOptimalLocalCullRect.top - upwardTrend, - oldOptimalLocalCullRect.right + rightwardTrend, - oldOptimalLocalCullRect.bottom + bottomwardTrend, + _exactLocalCullRect.left - _predictTrend(leftwardDelta, _exactLocalCullRect.width), + _exactLocalCullRect.top - _predictTrend(upwardDelta, _exactLocalCullRect.height), + _exactLocalCullRect.right + _predictTrend(rightwardDelta, _exactLocalCullRect.width), + _exactLocalCullRect.bottom + _predictTrend(bottomwardDelta, _exactLocalCullRect.height), ).intersect(localPaintBounds); final bool localCullRectChanged = _optimalLocalCullRect != newLocalCullRect; @@ -526,6 +520,25 @@ abstract class PersistedPicture extends PersistedLeafSurface { return localCullRectChanged; } + /// Predicts the delta a particular side of a clip rect will move given the + /// [delta] it moved by last, and the respective [extent] (width or height) + /// of the clip. + static double _predictTrend(double delta, double extent) { + if (delta <= 0.0) { + // Shrinking. Give it 10% of the extent in case the trend is reversed. + return extent * 0.1; + } else { + // Growing. Predict 10 more frames of similar deltas. Give it at least + // 50% of the extent (protect from extremely slow growth trend such as + // slow scrolling). Give no more than the full extent (protects from + // fast scrolling that could lead to overallocation). + return math.min( + math.max(extent * 0.5, delta * 10.0), + extent, + ); + } + } + /// Number of bitmap pixel painted by this picture. /// /// If the implementation does not paint onto a bitmap canvas, it should diff --git a/lib/web_ui/lib/src/engine/surface/recording_canvas.dart b/lib/web_ui/lib/src/engine/surface/recording_canvas.dart index 5b26210ae2af2..bb3f570450e94 100644 --- a/lib/web_ui/lib/src/engine/surface/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/surface/recording_canvas.dart @@ -23,10 +23,33 @@ double _measureBorderRadius(double x, double y) { /// /// See [Canvas] for docs for these methods. class RecordingCanvas { - /// Maximum paintable bounds for this canvas. + /// Computes [_pictureBounds]. final _PaintBounds _paintBounds; + + /// Maximum paintable bounds for the picture painted by this recording. + /// + /// The bounds contain the full picture. The commands recorded for the picture + /// are later pruned based on the clip applied to the picture. See the [apply] + /// method for more details. + ui.Rect get pictureBounds { + assert( + _debugRecordingEnded, + 'Picture bounds not available yet. Call [endRecording] before accessing picture bounds.', + ); + return _pictureBounds; + } + ui.Rect _pictureBounds; + final List _commands = []; + /// In debug mode returns the list of recorded paint commands for testing. + List get debugPaintCommands { + if (assertionsEnabled) { + return _commands; + } + throw UnsupportedError('For debugging only.'); + } + RecordingCanvas(ui.Rect bounds) : _paintBounds = _PaintBounds(bounds); /// Whether this canvas is doing arbitrary paint operations not expressible @@ -53,30 +76,78 @@ class RecordingCanvas { bool get didDraw => _didDraw; bool _didDraw = false; - /// Computes paint bounds based on estimated [bounds] and transforms. - ui.Rect computePaintBounds() { - return _paintBounds.computeBounds(); + /// When assertions are enabled used to ensure that [endRecording] is called + /// before calling [apply] or [pictureBounds]. + bool _debugRecordingEnded = false; + + /// Stops recording drawing commands and computes paint bounds. + /// + /// This must be called prior to passing the picture to the [SceneBuilder] + /// for rendering. In a production app, this is done automatically by + /// [PictureRecorder] when the framework calls [PictureRecorder.endRecording]. + /// However, if you are writing a unit-test and using [RecordingCanvas] + /// directly it is up to you to call this method explicitly. + void endRecording() { + _pictureBounds = _paintBounds.computeBounds(); + if (assertionsEnabled) { + _debugRecordingEnded = true; + } } /// Applies the recorded commands onto an [engineCanvas]. - void apply(EngineCanvas engineCanvas) { + /// + /// The [clipRect] specifies the clip applied to the picture (screen clip at + /// a minimum). The commands that fall outside the clip are skipped and are + /// not applied to the [engineCanvas]. A command must have a non-zero + /// intersection with the clip in order to be applied. + void apply(EngineCanvas engineCanvas, ui.Rect clipRect) { + assert(_debugRecordingEnded); if (_debugDumpPaintCommands) { final StringBuffer debugBuf = StringBuffer(); + int skips = 0; debugBuf.writeln( '--- Applying RecordingCanvas to ${engineCanvas.runtimeType} ' - 'with bounds $_paintBounds'); + 'with bounds $_paintBounds and clip $clipRect (w = ${clipRect.width},' + ' h = ${clipRect.height})'); for (int i = 0; i < _commands.length; i++) { final PaintCommand command = _commands[i]; + if (command is DrawCommand) { + if (command.isInvisible(clipRect)) { + // The drawing command is outside the clip region. No need to apply. + debugBuf.writeln('SKIPPED: ctx.$command;'); + skips += 1; + continue; + } + } debugBuf.writeln('ctx.$command;'); command.apply(engineCanvas); } + if (skips > 0) { + debugBuf.writeln('Total commands skipped: $skips'); + } debugBuf.writeln('--- End of command stream'); print(debugBuf); } else { try { - for (int i = 0, len = _commands.length; i < len; i++) { - PaintCommand command = _commands[i]; - command.apply(engineCanvas); + if (rectContainsOther(clipRect, _pictureBounds)) { + // No need to check if commands fit in the clip rect if we already + // know that the entire picture fits it. + for (int i = 0, len = _commands.length; i < len; i++) { + _commands[i].apply(engineCanvas); + } + } else { + // The picture doesn't fit the clip rect. Check that drawing commands + // fit before applying them. + for (int i = 0, len = _commands.length; i < len; i++) { + final PaintCommand command = _commands[i]; + if (command is DrawCommand) { + if (command.isInvisible(clipRect)) { + // The drawing command is outside the clip region. No need to apply. + continue; + } + } + command.apply(engineCanvas); + } } } catch (e) { // commands should never fail, but... @@ -103,12 +174,14 @@ class RecordingCanvas { } void save() { + assert(!_debugRecordingEnded); _paintBounds.saveTransformsAndClip(); _commands.add(const PaintSave()); _saveCount++; } void saveLayerWithoutBounds(SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; // TODO(het): Implement this correctly using another canvas. _commands.add(const PaintSave()); @@ -117,6 +190,7 @@ class RecordingCanvas { } void saveLayer(ui.Rect bounds, SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; // TODO(het): Implement this correctly using another canvas. _commands.add(const PaintSave()); @@ -125,6 +199,7 @@ class RecordingCanvas { } void restore() { + assert(!_debugRecordingEnded); _paintBounds.restoreTransformsAndClip(); if (_commands.isNotEmpty && _commands.last is PaintSave) { // A restore followed a save without any drawing operations in between. @@ -139,56 +214,71 @@ class RecordingCanvas { } void translate(double dx, double dy) { + assert(!_debugRecordingEnded); _paintBounds.translate(dx, dy); _commands.add(PaintTranslate(dx, dy)); } void scale(double sx, double sy) { + assert(!_debugRecordingEnded); _paintBounds.scale(sx, sy); _commands.add(PaintScale(sx, sy)); } void rotate(double radians) { + assert(!_debugRecordingEnded); _paintBounds.rotateZ(radians); _commands.add(PaintRotate(radians)); } void transform(Float64List matrix4) { + assert(!_debugRecordingEnded); _paintBounds.transform(matrix4); _commands.add(PaintTransform(matrix4)); } void skew(double sx, double sy) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _paintBounds.skew(sx, sy); _commands.add(PaintSkew(sx, sy)); } void clipRect(ui.Rect rect) { - _paintBounds.clipRect(rect); + assert(!_debugRecordingEnded); + final PaintClipRect command = PaintClipRect(rect); + _paintBounds.clipRect(rect, command); _hasArbitraryPaint = true; - _commands.add(PaintClipRect(rect)); + _commands.add(command); } void clipRRect(ui.RRect rrect) { - _paintBounds.clipRect(rrect.outerRect); + assert(!_debugRecordingEnded); + final PaintClipRRect command = PaintClipRRect(rrect); + _paintBounds.clipRect(rrect.outerRect, command); _hasArbitraryPaint = true; - _commands.add(PaintClipRRect(rrect)); + _commands.add(command); } void clipPath(ui.Path path, {bool doAntiAlias = true}) { - _paintBounds.clipRect(path.getBounds()); + assert(!_debugRecordingEnded); + final PaintClipPath command = PaintClipPath(path); + _paintBounds.clipRect(path.getBounds(), command); _hasArbitraryPaint = true; - _commands.add(PaintClipPath(path)); + _commands.add(command); } void drawColor(ui.Color color, ui.BlendMode blendMode) { - _paintBounds.grow(_paintBounds.maxPaintBounds); - _commands.add(PaintDrawColor(color, blendMode)); + assert(!_debugRecordingEnded); + final PaintDrawColor command = PaintDrawColor(color, blendMode); + _commands.add(command); + _paintBounds.grow(_paintBounds.maxPaintBounds, command); } void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaint paint) { + assert(!_debugRecordingEnded); final double paintSpread = math.max(_getPaintSpread(paint), 1.0); + final PaintDrawLine command = PaintDrawLine(p1, p2, paint.paintData); // TODO(yjbanov): This can be optimized. Currently we create a box around // the line and then apply the transform on the box to get // the bounding box. If you have a 45-degree line and a @@ -201,34 +291,40 @@ class RecordingCanvas { math.min(p1.dy, p2.dy) - paintSpread, math.max(p1.dx, p2.dx) + paintSpread, math.max(p1.dy, p2.dy) + paintSpread, + command, ); _hasArbitraryPaint = true; _didDraw = true; - _commands.add(PaintDrawLine(p1, p2, paint.paintData)); + _commands.add(command); } void drawPaint(SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _didDraw = true; - _paintBounds.grow(_paintBounds.maxPaintBounds); - _commands.add(PaintDrawPaint(paint.paintData)); + final PaintDrawPaint command = PaintDrawPaint(paint.paintData); + _paintBounds.grow(_paintBounds.maxPaintBounds, command); + _commands.add(command); } void drawRect(ui.Rect rect, SurfacePaint paint) { + assert(!_debugRecordingEnded); if (paint.shader != null) { _hasArbitraryPaint = true; } _didDraw = true; final double paintSpread = _getPaintSpread(paint); + final PaintDrawRect command = PaintDrawRect(rect, paint.paintData); if (paintSpread != 0.0) { - _paintBounds.grow(rect.inflate(paintSpread)); + _paintBounds.grow(rect.inflate(paintSpread), command); } else { - _paintBounds.grow(rect); + _paintBounds.grow(rect, command); } - _commands.add(PaintDrawRect(rect, paint.paintData)); + _commands.add(command); } void drawRRect(ui.RRect rrect, SurfacePaint paint) { + assert(!_debugRecordingEnded); if (paint.shader != null || !rrect.webOnlyUniformRadii) { _hasArbitraryPaint = true; } @@ -238,11 +334,13 @@ class RecordingCanvas { final double top = math.min(rrect.top, rrect.bottom) - paintSpread; final double right = math.max(rrect.left, rrect.right) + paintSpread; final double bottom = math.max(rrect.top, rrect.bottom) + paintSpread; - _paintBounds.growLTRB(left, top, right, bottom); - _commands.add(PaintDrawRRect(rrect, paint.paintData)); + final PaintDrawRRect command = PaintDrawRRect(rrect, paint.paintData); + _paintBounds.growLTRB(left, top, right, bottom, command); + _commands.add(command); } void drawDRRect(ui.RRect outer, ui.RRect inner, SurfacePaint paint) { + assert(!_debugRecordingEnded); // Check the inner bounds are contained within the outer bounds // see: https://cs.chromium.org/chromium/src/third_party/skia/src/core/SkCanvas.cpp?l=1787-1789 ui.Rect innerRect = inner.outerRect; @@ -283,41 +381,50 @@ class RecordingCanvas { _hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); + final PaintDrawDRRect command = PaintDrawDRRect(outer, inner, paint.paintData); _paintBounds.growLTRB( outer.left - paintSpread, outer.top - paintSpread, outer.right + paintSpread, outer.bottom + paintSpread, + command, ); - _commands.add(PaintDrawDRRect(outer, inner, paint.paintData)); + _commands.add(command); } void drawOval(ui.Rect rect, SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); + final PaintDrawOval command = PaintDrawOval(rect, paint.paintData); if (paintSpread != 0.0) { - _paintBounds.grow(rect.inflate(paintSpread)); + _paintBounds.grow(rect.inflate(paintSpread), command); } else { - _paintBounds.grow(rect); + _paintBounds.grow(rect, command); } - _commands.add(PaintDrawOval(rect, paint.paintData)); + _commands.add(command); } void drawCircle(ui.Offset c, double radius, SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); + final PaintDrawCircle command = PaintDrawCircle(c, radius, paint.paintData); + final double distance = radius + paintSpread; _paintBounds.growLTRB( - c.dx - radius - paintSpread, - c.dy - radius - paintSpread, - c.dx + radius + paintSpread, - c.dy + radius + paintSpread, + c.dx - distance, + c.dy - distance, + c.dx + distance, + c.dy + distance, + command, ); - _commands.add(PaintDrawCircle(c, radius, paint.paintData)); + _commands.add(command); } void drawPath(ui.Path path, SurfacePaint paint) { + assert(!_debugRecordingEnded); if (paint.shader == null) { // For Rect/RoundedRect paths use drawRect/drawRRect code paths for // DomCanvas optimization. @@ -340,31 +447,37 @@ class RecordingCanvas { if (paintSpread != 0.0) { pathBounds = pathBounds.inflate(paintSpread); } - _paintBounds.grow(pathBounds); // Clone path so it can be reused for subsequent draw calls. final ui.Path clone = SurfacePath._shallowCopy(path); + final PaintDrawPath command = PaintDrawPath(clone, paint.paintData); + _paintBounds.grow(pathBounds, command); clone.fillType = path.fillType; - _commands.add(PaintDrawPath(clone, paint.paintData)); + _commands.add(command); } void drawImage(ui.Image image, ui.Offset offset, SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _didDraw = true; final double left = offset.dx; final double top = offset.dy; - _paintBounds.growLTRB(left, top, left + image.width, top + image.height); - _commands.add(PaintDrawImage(image, offset, paint.paintData)); + final command = PaintDrawImage(image, offset, paint.paintData); + _paintBounds.growLTRB(left, top, left + image.width, top + image.height, command); + _commands.add(command); } void drawImageRect( ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _didDraw = true; - _paintBounds.grow(dst); - _commands.add(PaintDrawImageRect(image, src, dst, paint.paintData)); + final PaintDrawImageRect command = PaintDrawImageRect(image, src, dst, paint.paintData); + _paintBounds.grow(dst, command); + _commands.add(command); } void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { + assert(!_debugRecordingEnded); final EngineParagraph engineParagraph = paragraph; if (!engineParagraph._isLaidOut) { // Ignore non-laid out paragraphs. This matches Flutter's behavior. @@ -377,42 +490,53 @@ class RecordingCanvas { } final double left = offset.dx; final double top = offset.dy; + final PaintDrawParagraph command = PaintDrawParagraph(engineParagraph, offset); _paintBounds.growLTRB( - left, top, left + engineParagraph.width, top + engineParagraph.height); - _commands.add(PaintDrawParagraph(engineParagraph, offset)); + left, + top, + left + engineParagraph.width, + top + engineParagraph.height, + command, + ); + _commands.add(command); } void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _didDraw = true; final ui.Rect shadowRect = computePenumbraBounds(path.getBounds(), elevation); - _paintBounds.grow(shadowRect); - _commands.add(PaintDrawShadow(path, color, elevation, transparentOccluder)); + final PaintDrawShadow command = PaintDrawShadow(path, color, elevation, transparentOccluder); + _paintBounds.grow(shadowRect, command); + _commands.add(command); } void drawVertices( ui.Vertices vertices, ui.BlendMode blendMode, SurfacePaint paint) { + assert(!_debugRecordingEnded); _hasArbitraryPaint = true; _didDraw = true; - _growPaintBoundsByPoints(vertices.positions, 0, paint); - _commands.add(PaintVertices(vertices, blendMode, paint.paintData)); + final PaintDrawVertices command = PaintDrawVertices(vertices, blendMode, paint.paintData); + _growPaintBoundsByPoints(vertices.positions, 0, paint, command); + _commands.add(command); } void drawRawPoints( ui.PointMode pointMode, Float32List points, SurfacePaint paint) { + assert(!_debugRecordingEnded); if (paint.strokeWidth == null) { return; } _hasArbitraryPaint = true; _didDraw = true; - _growPaintBoundsByPoints(points, paint.strokeWidth, paint); - _commands - .add(PaintPoints(pointMode, points, paint.strokeWidth, paint.color)); + final PaintDrawPoints command = PaintDrawPoints(pointMode, points, paint.strokeWidth, paint.color); + _growPaintBoundsByPoints(points, paint.strokeWidth, paint, command); + _commands.add(command); } - void _growPaintBoundsByPoints(Float32List points, double thickness, SurfacePaint paint) { + void _growPaintBoundsByPoints(Float32List points, double thickness, SurfacePaint paint, DrawCommand command) { double minValueX, maxValueX, minValueY, maxValueY; minValueX = maxValueX = points[0]; minValueY = maxValueY = points[1]; @@ -436,6 +560,7 @@ class RecordingCanvas { minValueY - distance - paintSpread, maxValueX + distance + paintSpread, maxValueY + distance + paintSpread, + command, ); } @@ -458,6 +583,43 @@ abstract class PaintCommand { void serializeToCssPaint(List> serializedCommands); } +/// A [PaintCommand] that affect pixels on the screen (unlike, for example, the +/// [SaveCommand]). +abstract class DrawCommand extends PaintCommand { + /// Whether the command is completely clipped out of the picture. + bool isClippedOut = false; + + /// The left bound of the graphic produced by this command in picture-global + /// coordinates. + double leftBound = double.negativeInfinity; + + /// The top bound of the graphic produced by this command in picture-global + /// coordinates. + double topBound = double.negativeInfinity; + + /// The right bound of the graphic produced by this command in picture-global + /// coordinates. + double rightBound = double.infinity; + + /// The bottom bound of the graphic produced by this command in + /// picture-global coordinates. + double bottomBound = double.infinity; + + /// Whether this command intersects with the [clipRect]. + bool isInvisible(ui.Rect clipRect) { + if (isClippedOut) { + return true; + } + + // Check top and bottom first because vertical scrolling is more common + // than horizontal scrolling. + return bottomBound < clipRect.top || + topBound > clipRect.bottom || + rightBound < clipRect.left || + leftBound > clipRect.right; + } +} + class PaintSave extends PaintCommand { const PaintSave(); @@ -632,7 +794,7 @@ class PaintSkew extends PaintCommand { } } -class PaintClipRect extends PaintCommand { +class PaintClipRect extends DrawCommand { final ui.Rect rect; PaintClipRect(this.rect); @@ -657,7 +819,7 @@ class PaintClipRect extends PaintCommand { } } -class PaintClipRRect extends PaintCommand { +class PaintClipRRect extends DrawCommand { final ui.RRect rrect; PaintClipRRect(this.rrect); @@ -685,7 +847,7 @@ class PaintClipRRect extends PaintCommand { } } -class PaintClipPath extends PaintCommand { +class PaintClipPath extends DrawCommand { final SurfacePath path; PaintClipPath(this.path); @@ -710,7 +872,7 @@ class PaintClipPath extends PaintCommand { } } -class PaintDrawColor extends PaintCommand { +class PaintDrawColor extends DrawCommand { final ui.Color color; final ui.BlendMode blendMode; @@ -737,7 +899,7 @@ class PaintDrawColor extends PaintCommand { } } -class PaintDrawLine extends PaintCommand { +class PaintDrawLine extends DrawCommand { final ui.Offset p1; final ui.Offset p2; final SurfacePaintData paint; @@ -771,7 +933,7 @@ class PaintDrawLine extends PaintCommand { } } -class PaintDrawPaint extends PaintCommand { +class PaintDrawPaint extends DrawCommand { final SurfacePaintData paint; PaintDrawPaint(this.paint); @@ -796,11 +958,11 @@ class PaintDrawPaint extends PaintCommand { } } -class PaintVertices extends PaintCommand { +class PaintDrawVertices extends DrawCommand { final ui.Vertices vertices; final ui.BlendMode blendMode; final SurfacePaintData paint; - PaintVertices(this.vertices, this.blendMode, this.paint); + PaintDrawVertices(this.vertices, this.blendMode, this.paint); @override void apply(EngineCanvas canvas) { @@ -822,12 +984,12 @@ class PaintVertices extends PaintCommand { } } -class PaintPoints extends PaintCommand { +class PaintDrawPoints extends DrawCommand { final Float32List points; final ui.PointMode pointMode; final double strokeWidth; final ui.Color color; - PaintPoints(this.pointMode, this.points, this.strokeWidth, this.color); + PaintDrawPoints(this.pointMode, this.points, this.strokeWidth, this.color); @override void apply(EngineCanvas canvas) { @@ -849,7 +1011,7 @@ class PaintPoints extends PaintCommand { } } -class PaintDrawRect extends PaintCommand { +class PaintDrawRect extends DrawCommand { final ui.Rect rect; final SurfacePaintData paint; @@ -879,7 +1041,7 @@ class PaintDrawRect extends PaintCommand { } } -class PaintDrawRRect extends PaintCommand { +class PaintDrawRRect extends DrawCommand { final ui.RRect rrect; final SurfacePaintData paint; @@ -909,7 +1071,7 @@ class PaintDrawRRect extends PaintCommand { } } -class PaintDrawDRRect extends PaintCommand { +class PaintDrawDRRect extends DrawCommand { final ui.RRect outer; final ui.RRect inner; final SurfacePaintData paint; @@ -941,7 +1103,7 @@ class PaintDrawDRRect extends PaintCommand { } } -class PaintDrawOval extends PaintCommand { +class PaintDrawOval extends DrawCommand { final ui.Rect rect; final SurfacePaintData paint; @@ -971,7 +1133,7 @@ class PaintDrawOval extends PaintCommand { } } -class PaintDrawCircle extends PaintCommand { +class PaintDrawCircle extends DrawCommand { final ui.Offset c; final double radius; final SurfacePaintData paint; @@ -1004,7 +1166,7 @@ class PaintDrawCircle extends PaintCommand { } } -class PaintDrawPath extends PaintCommand { +class PaintDrawPath extends DrawCommand { final SurfacePath path; final SurfacePaintData paint; @@ -1034,7 +1196,7 @@ class PaintDrawPath extends PaintCommand { } } -class PaintDrawShadow extends PaintCommand { +class PaintDrawShadow extends DrawCommand { PaintDrawShadow( this.path, this.color, this.elevation, this.transparentOccluder); @@ -1074,7 +1236,7 @@ class PaintDrawShadow extends PaintCommand { } } -class PaintDrawImage extends PaintCommand { +class PaintDrawImage extends DrawCommand { final ui.Image image; final ui.Offset offset; final SurfacePaintData paint; @@ -1103,7 +1265,7 @@ class PaintDrawImage extends PaintCommand { } } -class PaintDrawImageRect extends PaintCommand { +class PaintDrawImageRect extends DrawCommand { final ui.Image image; final ui.Rect src; final ui.Rect dst; @@ -1133,7 +1295,7 @@ class PaintDrawImageRect extends PaintCommand { } } -class PaintDrawParagraph extends PaintCommand { +class PaintDrawParagraph extends DrawCommand { final EngineParagraph paragraph; final ui.Offset offset; @@ -1765,7 +1927,7 @@ class _PaintBounds { _currentMatrix.multiply(skewMatrix); } - void clipRect(ui.Rect rect) { + void clipRect(ui.Rect rect, DrawCommand command) { // If we have an active transform, calculate screen relative clipping // rectangle and union with current clipping rectangle. if (!_currentMatrixIsIdentity) { @@ -1807,16 +1969,25 @@ class _PaintBounds { _currentClipBottom = rect.bottom; } } + if (_currentClipLeft >= _currentClipRight || _currentClipTop >= _currentClipBottom) { + command.isClippedOut = true; + } else { + command.leftBound = _currentClipLeft; + command.topBound = _currentClipTop; + command.rightBound = _currentClipRight; + command.bottomBound = _currentClipBottom; + } } /// Grow painted area to include given rectangle. - void grow(ui.Rect r) { - growLTRB(r.left, r.top, r.right, r.bottom); + void grow(ui.Rect r, DrawCommand command) { + growLTRB(r.left, r.top, r.right, r.bottom, command); } /// Grow painted area to include given rectangle. - void growLTRB(double left, double top, double right, double bottom) { + void growLTRB(double left, double top, double right, double bottom, DrawCommand command) { if (left == right || top == bottom) { + command.isClippedOut = true; return; } @@ -1836,15 +2007,19 @@ class _PaintBounds { if (_clipRectInitialized) { if (transformedPointLeft > _currentClipRight) { + command.isClippedOut = true; return; } if (transformedPointRight < _currentClipLeft) { + command.isClippedOut = true; return; } if (transformedPointTop > _currentClipBottom) { + command.isClippedOut = true; return; } if (transformedPointBottom < _currentClipTop) { + command.isClippedOut = true; return; } if (transformedPointLeft < _currentClipLeft) { @@ -1861,6 +2036,11 @@ class _PaintBounds { } } + command.leftBound = transformedPointLeft; + command.topBound = transformedPointTop; + command.rightBound = transformedPointRight; + command.bottomBound = transformedPointBottom; + if (_didPaintInsideClipArea) { _left = math.min( math.min(_left, transformedPointLeft), transformedPointRight); diff --git a/lib/web_ui/test/canvas_test.dart b/lib/web_ui/test/canvas_test.dart index ce9d1499caca2..cb7bcd2fbcf57 100644 --- a/lib/web_ui/test/canvas_test.dart +++ b/lib/web_ui/test/canvas_test.dart @@ -36,16 +36,17 @@ void main() { } testCanvas('draws laid out paragraph', (EngineCanvas canvas) { - final RecordingCanvas recordingCanvas = - RecordingCanvas(const ui.Rect.fromLTWH(0, 0, 100, 100)); + final ui.Rect screenRect = const ui.Rect.fromLTWH(0, 0, 100, 100); + final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); builder.addText('sample'); paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 100)); recordingCanvas.drawParagraph(paragraph, const ui.Offset(10, 10)); + recordingCanvas.endRecording(); canvas.clear(); - recordingCanvas.apply(canvas); + recordingCanvas.apply(canvas, screenRect); }, whenDone: () { expect(mockCanvas.methodCallLog, hasLength(3)); @@ -60,15 +61,16 @@ void main() { testCanvas('ignores paragraphs that were not laid out', (EngineCanvas canvas) { - final RecordingCanvas recordingCanvas = - RecordingCanvas(const ui.Rect.fromLTWH(0, 0, 100, 100)); + final ui.Rect screenRect = const ui.Rect.fromLTWH(0, 0, 100, 100); + final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); builder.addText('sample'); final ui.Paragraph paragraph = builder.build(); recordingCanvas.drawParagraph(paragraph, const ui.Offset(10, 10)); + recordingCanvas.endRecording(); canvas.clear(); - recordingCanvas.apply(canvas); + recordingCanvas.apply(canvas, screenRect); }, whenDone: () { expect(mockCanvas.methodCallLog, hasLength(2)); expect(mockCanvas.methodCallLog[0].methodName, 'clear'); diff --git a/lib/web_ui/test/engine/recording_canvas_test.dart b/lib/web_ui/test/engine/recording_canvas_test.dart index 2757ba96fd4a4..5610c9c1f4de8 100644 --- a/lib/web_ui/test/engine/recording_canvas_test.dart +++ b/lib/web_ui/test/engine/recording_canvas_test.dart @@ -12,9 +12,10 @@ import '../mock_engine_canvas.dart'; void main() { RecordingCanvas underTest; MockEngineCanvas mockCanvas; + final Rect screenRect = Rect.largest; setUp(() { - underTest = RecordingCanvas(Rect.largest); + underTest = RecordingCanvas(screenRect); mockCanvas = MockEngineCanvas(); }); @@ -25,9 +26,10 @@ void main() { test('Happy case', () { underTest.drawDRRect(rrect, rrect.deflate(1), somePaint); - underTest.apply(mockCanvas); + underTest.endRecording(); + underTest.apply(mockCanvas, screenRect); - _expectDrawCall(mockCanvas, { + _expectDrawDRRectCall(mockCanvas, { 'outer': rrect, 'inner': rrect.deflate(1), 'paint': somePaint.paintData, @@ -36,7 +38,8 @@ void main() { test('Inner RRect > Outer RRect', () { underTest.drawDRRect(rrect, rrect.inflate(1), somePaint); - underTest.apply(mockCanvas); + underTest.endRecording(); + underTest.apply(mockCanvas, screenRect); // Expect nothing to be called expect(mockCanvas.methodCallLog.length, equals(1)); expect(mockCanvas.methodCallLog.single.methodName, 'endOfPaint'); @@ -45,7 +48,8 @@ void main() { test('Inner RRect not completely inside Outer RRect', () { underTest.drawDRRect( rrect, rrect.deflate(1).shift(const Offset(0.0, 10)), somePaint); - underTest.apply(mockCanvas); + underTest.endRecording(); + underTest.apply(mockCanvas, screenRect); // Expect nothing to be called expect(mockCanvas.methodCallLog.length, equals(1)); expect(mockCanvas.methodCallLog.single.methodName, 'endOfPaint'); @@ -53,7 +57,8 @@ void main() { test('Inner RRect same as Outer RRect', () { underTest.drawDRRect(rrect, rrect, somePaint); - underTest.apply(mockCanvas); + underTest.endRecording(); + underTest.apply(mockCanvas, screenRect); // Expect nothing to be called expect(mockCanvas.methodCallLog.length, equals(1)); expect(mockCanvas.methodCallLog.single.methodName, 'endOfPaint'); @@ -72,10 +77,11 @@ void main() { expect(inner.trRadius, equals(Radius.circular(-1))); underTest.drawDRRect(outer, inner, somePaint); - underTest.apply(mockCanvas); + underTest.endRecording(); + underTest.apply(mockCanvas, screenRect); // Expect to draw, even when inner has negative radii (which get ignored by canvas) - _expectDrawCall(mockCanvas, { + _expectDrawDRRectCall(mockCanvas, { 'outer': outer, 'inner': inner, 'paint': somePaint.paintData, @@ -89,19 +95,105 @@ void main() { RRect.fromRectAndCorners(const Rect.fromLTRB(12, 22, 28, 38)); underTest.drawDRRect(outer, inner, somePaint); - underTest.apply(mockCanvas); + underTest.endRecording(); + underTest.apply(mockCanvas, screenRect); - _expectDrawCall(mockCanvas, { + _expectDrawDRRectCall(mockCanvas, { 'outer': outer, 'inner': inner, 'paint': somePaint.paintData, }); }); }); + + test('Filters out paint commands outside the clip rect', () { + // Outside to the left + underTest.drawRect(Rect.fromLTWH(0.0, 20.0, 10.0, 10.0), Paint()); + + // Outside above + underTest.drawRect(Rect.fromLTWH(20.0, 0.0, 10.0, 10.0), Paint()); + + // Visible + underTest.drawRect(Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), Paint()); + + // Inside the layer clip rect but zero-size + underTest.drawRect(Rect.fromLTRB(20.0, 20.0, 30.0, 20.0), Paint()); + + // Inside the layer clip but clipped out by a canvas clip + underTest.save(); + underTest.clipRect(Rect.fromLTWH(0, 0, 10, 10)); + underTest.drawRect(Rect.fromLTWH(20.0, 20.0, 10.0, 10.0), Paint()); + underTest.restore(); + + // Outside to the right + underTest.drawRect(Rect.fromLTWH(40.0, 20.0, 10.0, 10.0), Paint()); + + // Outside below + underTest.drawRect(Rect.fromLTWH(20.0, 40.0, 10.0, 10.0), Paint()); + + underTest.endRecording(); + + expect(underTest.debugPaintCommands, hasLength(10)); + final PaintDrawRect outsideLeft = underTest.debugPaintCommands[0]; + expect(outsideLeft.isClippedOut, false); + expect(outsideLeft.leftBound, 0); + expect(outsideLeft.topBound, 20); + expect(outsideLeft.rightBound, 10); + expect(outsideLeft.bottomBound, 30); + + final PaintDrawRect outsideAbove = underTest.debugPaintCommands[1]; + expect(outsideAbove.isClippedOut, false); + + final PaintDrawRect visible = underTest.debugPaintCommands[2]; + expect(visible.isClippedOut, false); + + final PaintDrawRect zeroSize = underTest.debugPaintCommands[3]; + expect(zeroSize.isClippedOut, true); + + expect(underTest.debugPaintCommands[4], isA()); + + final PaintClipRect clip = underTest.debugPaintCommands[5]; + expect(clip.isClippedOut, false); + + final PaintDrawRect clippedOut = underTest.debugPaintCommands[6]; + expect(clippedOut.isClippedOut, true); + + expect(underTest.debugPaintCommands[7], isA()); + + final PaintDrawRect outsideRight = underTest.debugPaintCommands[8]; + expect(outsideRight.isClippedOut, false); + + final PaintDrawRect outsideBelow = underTest.debugPaintCommands[9]; + expect(outsideBelow.isClippedOut, false); + + // Give it the entire screen so everything paints. + underTest.apply(mockCanvas, screenRect); + expect(mockCanvas.methodCallLog, hasLength(11)); + expect(mockCanvas.methodCallLog[0].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[1].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[2].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[3].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[4].methodName, 'save'); + expect(mockCanvas.methodCallLog[5].methodName, 'clipRect'); + expect(mockCanvas.methodCallLog[6].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[7].methodName, 'restore'); + expect(mockCanvas.methodCallLog[8].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[9].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[10].methodName, 'endOfPaint'); + + // Clip out a middle region that only contains 'drawRect' + mockCanvas.methodCallLog.clear(); + underTest.apply(mockCanvas, Rect.fromLTRB(15, 15, 35, 35)); + expect(mockCanvas.methodCallLog, hasLength(4)); + expect(mockCanvas.methodCallLog[0].methodName, 'drawRect'); + expect(mockCanvas.methodCallLog[1].methodName, 'save'); + expect(mockCanvas.methodCallLog[2].methodName, 'restore'); + expect(mockCanvas.methodCallLog[3].methodName, 'endOfPaint'); + }); } // Expect a drawDRRect call to be registered in the mock call log, with the expectedArguments -void _expectDrawCall( +void _expectDrawDRRectCall( MockEngineCanvas mock, Map expectedArguments) { expect(mock.methodCallLog.length, equals(2)); MockCanvasCall mockCall = mock.methodCallLog[0]; diff --git a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart index 694274bfe516e..8407e48766b33 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_blend_golden_test.dart @@ -23,7 +23,8 @@ void main() async { double maxDiffRatePercent = 0.0}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart index 316b6b3467fde..d01620921c1d7 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_clip_path_test.dart @@ -22,7 +22,8 @@ void main() async { {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { final engine.EngineCanvas engineCanvas = engine.BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart index f0d2f15a08a77..927bf2a300fbd 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_context_test.dart @@ -22,7 +22,8 @@ void main() async { {Rect region = const Rect.fromLTWH(0, 0, 500, 500)}) async { final engine.EngineCanvas engineCanvas = engine.BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart index dcaf6c93b59bd..6a61f401b25bd 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_draw_image_golden_test.dart @@ -26,7 +26,8 @@ void main() async { double maxDiffRatePercent = 0.0}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart b/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart index e92f55287a252..469cc2f88faf8 100644 --- a/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart +++ b/lib/web_ui/test/golden_tests/engine/canvas_reuse_test.dart @@ -39,7 +39,8 @@ void main() async { ..moveTo(3, 0) ..lineTo(100, 97); rc.drawPath(path, testPaint); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); engineCanvas.endOfPaint(); html.Element sceneElement = html.Element.tag('flt-scene'); @@ -69,7 +70,8 @@ void main() async { ..quadraticBezierTo(100, 0, 100, 100); rc2.drawImage(_createRealTestImage(), Offset(0, 0), Paint()); rc2.drawPath(path2, testPaint); - rc2.apply(engineCanvas); + rc2.endRecording(); + rc2.apply(engineCanvas, screenRect); sceneElement = html.Element.tag('flt-scene'); sceneElement.append(engineCanvas.rootElement); diff --git a/lib/web_ui/test/golden_tests/engine/conic_golden_test.dart b/lib/web_ui/test/golden_tests/engine/conic_golden_test.dart index 0ec9770603d40..bcf69c155e212 100644 --- a/lib/web_ui/test/golden_tests/engine/conic_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/conic_golden_test.dart @@ -31,9 +31,10 @@ void main() async { ..style = PaintingStyle.stroke; canvas.drawPath(path, paint); + canvas.endRecording(); html.document.body.append(bitmapCanvas.rootElement); - canvas.apply(bitmapCanvas); + canvas.apply(bitmapCanvas, canvasBounds); await matchGoldenFile('$scubaFileName.png', region: region); bitmapCanvas.rootElement.remove(); } diff --git a/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart b/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart index dc77dbc781ae8..765c614ae4cfc 100644 --- a/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/draw_vertices_golden_test.dart @@ -22,7 +22,8 @@ void main() async { {Rect region = const Rect.fromLTWH(0, 0, 500, 500), bool write = false}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart b/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart index c535719597045..35b7cae8f9ecf 100644 --- a/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/linear_gradient_golden_test.dart @@ -21,7 +21,8 @@ void main() async { {Rect region = const Rect.fromLTWH(0, 0, 500, 500), bool write = false}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/multiline_text_clipping_golden_test.dart b/lib/web_ui/test/golden_tests/engine/multiline_text_clipping_golden_test.dart index cec631431b49d..cd3378b569896 100644 --- a/lib/web_ui/test/golden_tests/engine/multiline_text_clipping_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/multiline_text_clipping_golden_test.dart @@ -21,10 +21,11 @@ void main() async { setUpStableTestFonts(); void paintTest(EngineCanvas canvas, PaintTest painter) { - final RecordingCanvas recordingCanvas = - RecordingCanvas(const Rect.fromLTWH(0, 0, 600, 600)); + final Rect screenRect = const Rect.fromLTWH(0, 0, 600, 600); + final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); painter(recordingCanvas); - recordingCanvas.apply(canvas); + recordingCanvas.endRecording(); + recordingCanvas.apply(canvas, screenRect); } testEachCanvas( diff --git a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart index 38379087867e2..b5609c72a43fd 100644 --- a/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart +++ b/lib/web_ui/test/golden_tests/engine/path_metrics_test.dart @@ -25,7 +25,8 @@ void main() async { {Rect region = const Rect.fromLTWH(0, 0, 500, 500), bool write = false}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/path_to_svg_golden_test.dart b/lib/web_ui/test/golden_tests/engine/path_to_svg_golden_test.dart index c4a1d07a76e9b..bbfd9df35fc6b 100644 --- a/lib/web_ui/test/golden_tests/engine/path_to_svg_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/path_to_svg_golden_test.dart @@ -37,7 +37,8 @@ void main() async { html.document.body.append(bitmapCanvas.rootElement); html.document.body.append(svgElement); - canvas.apply(bitmapCanvas); + canvas.endRecording(); + canvas.apply(bitmapCanvas, canvasBounds); await matchGoldenFile('$scubaFileName.png', region: region); diff --git a/lib/web_ui/test/golden_tests/engine/path_transform_test.dart b/lib/web_ui/test/golden_tests/engine/path_transform_test.dart index dd53ae3153a17..342ecfd20da68 100644 --- a/lib/web_ui/test/golden_tests/engine/path_transform_test.dart +++ b/lib/web_ui/test/golden_tests/engine/path_transform_test.dart @@ -22,7 +22,8 @@ void main() async { {Rect region = const Rect.fromLTWH(0, 0, 500, 500), bool write = false}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/radial_gradient_golden_test.dart b/lib/web_ui/test/golden_tests/engine/radial_gradient_golden_test.dart index 3e40f57aa37c5..4fb58ebc3df94 100644 --- a/lib/web_ui/test/golden_tests/engine/radial_gradient_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/radial_gradient_golden_test.dart @@ -21,7 +21,8 @@ void main() async { {Rect region = const Rect.fromLTWH(0, 0, 500, 500), bool write = false}) async { final EngineCanvas engineCanvas = BitmapCanvas(screenRect); - rc.apply(engineCanvas); + rc.endRecording(); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); diff --git a/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart b/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart index c015a1ea4ec45..6b842b5267c61 100644 --- a/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart +++ b/lib/web_ui/test/golden_tests/engine/recording_canvas_golden_test.dart @@ -30,7 +30,7 @@ void main() async { engineCanvas ..save() ..drawRect( - rc.computePaintBounds(), + rc.pictureBounds, SurfacePaintData() ..color = const Color.fromRGBO(0, 0, 255, 1.0) ..style = PaintingStyle.stroke @@ -38,7 +38,7 @@ void main() async { ) ..restore(); - rc.apply(engineCanvas); + rc.apply(engineCanvas, screenRect); // Wrap in so that our CSS selectors kick in. final html.Element sceneElement = html.Element.tag('flt-scene'); @@ -63,15 +63,17 @@ void main() async { test('Empty canvas reports correct paint bounds', () async { final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); - expect(rc.computePaintBounds(), Rect.zero); + rc.endRecording(); + expect(rc.pictureBounds, Rect.zero); await _checkScreenshot(rc, 'empty_canvas'); }); test('Computes paint bounds for draw line', () async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawLine(const Offset(50, 100), const Offset(120, 140), testPaint); + rc.endRecording(); // The off by one is due to the minimum stroke width of 1. - expect(rc.computePaintBounds(), const Rect.fromLTRB(49, 99, 121, 141)); + expect(rc.pictureBounds, const Rect.fromLTRB(49, 99, 121, 141)); await _checkScreenshot(rc, 'draw_line'); }); @@ -81,8 +83,9 @@ void main() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawLine(const Offset(50, 100), const Offset(screenWidth + 100.0, 140), testPaint); + rc.endRecording(); // The off by one is due to the minimum stroke width of 1. - expect(rc.computePaintBounds(), + expect(rc.pictureBounds, const Rect.fromLTRB(49.0, 99.0, screenWidth, 141.0)); await _checkScreenshot(rc, 'draw_line_exceeding_limits'); }); @@ -90,7 +93,8 @@ void main() async { test('Computes paint bounds for draw rect', () async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(10, 20, 30, 40)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); await _checkScreenshot(rc, 'draw_rect'); }); @@ -100,12 +104,14 @@ void main() async { rc.drawRect( const Rect.fromLTRB(10, 20, 30 + screenWidth, 40 + screenHeight), testPaint); - expect(rc.computePaintBounds(), + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, screenWidth, screenHeight)); rc = RecordingCanvas(screenRect); rc.drawRect(const Rect.fromLTRB(-200, -100, 30, 40), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(0, 0, 30, 40)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(0, 0, 30, 40)); await _checkScreenshot(rc, 'draw_rect_exceeding_limits'); }); @@ -113,7 +119,8 @@ void main() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.translate(5, 7); rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(15, 27, 35, 47)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(15, 27, 35, 47)); await _checkScreenshot(rc, 'translate'); }); @@ -121,7 +128,8 @@ void main() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.scale(2, 2); rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(20, 40, 60, 80)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(20, 40, 60, 80)); await _checkScreenshot(rc, 'scale'); }); @@ -130,8 +138,9 @@ void main() async { rc.rotate(math.pi / 4.0); rc.drawLine( const Offset(1, 0), Offset(50 * math.sqrt(2) - 1, 0), testPaint); + rc.endRecording(); // The extra 0.7 is due to stroke width of 1 rotated by 45 degrees. - expect(rc.computePaintBounds(), + expect(rc.pictureBounds, within(distance: 0.1, from: const Rect.fromLTRB(0, 0, 50.7, 50.7))); await _checkScreenshot(rc, 'rotate'); }); @@ -140,8 +149,9 @@ void main() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.skew(1.0, 0.0); rc.drawRect(const Rect.fromLTRB(20, 20, 40, 40), testPaint); + rc.endRecording(); expect( - rc.computePaintBounds(), + rc.pictureBounds, within( distance: 0.1, from: const Rect.fromLTRB(40.0, 20.0, 80.0, 40.0))); await _checkScreenshot(rc, 'skew_horizontally'); @@ -151,8 +161,9 @@ void main() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.skew(0.0, 1.0); rc.drawRect(const Rect.fromLTRB(20, 20, 40, 40), testPaint); + rc.endRecording(); expect( - rc.computePaintBounds(), + rc.pictureBounds, within( distance: 0.1, from: const Rect.fromLTRB(20.0, 40.0, 40.0, 80.0))); await _checkScreenshot(rc, 'skew_vertically'); @@ -180,7 +191,8 @@ void main() async { matrix[15] = 1.0; rc.transform(matrix); rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - expect(rc.computePaintBounds(), + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(168.0, 283.6, 224.0, 368.4)); await _checkScreenshot(rc, 'complex_transform'); }); @@ -189,7 +201,8 @@ void main() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawPaint(testPaint); rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - expect(rc.computePaintBounds(), screenRect); + rc.endRecording(); + expect(rc.pictureBounds, screenRect); await _checkScreenshot(rc, 'draw_paint'); }); @@ -199,14 +212,16 @@ void main() async { rc.drawRect(const Rect.fromLTRB(10, 20, 30, 40), testPaint); rc.drawColor(const Color(0xFFFF0000), BlendMode.multiply); rc.drawRect(const Rect.fromLTRB(10, 60, 30, 80), testPaint); - expect(rc.computePaintBounds(), screenRect); + rc.endRecording(); + expect(rc.pictureBounds, screenRect); await _checkScreenshot(rc, 'draw_color'); }); test('Computes paint bounds for draw oval', () async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawOval(const Rect.fromLTRB(10, 20, 30, 40), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(10, 20, 30, 40)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); await _checkScreenshot(rc, 'draw_oval'); }); @@ -216,7 +231,8 @@ void main() async { RRect.fromRectAndRadius( const Rect.fromLTRB(10, 20, 30, 40), const Radius.circular(5.0)), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(10, 20, 30, 40)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); await _checkScreenshot(rc, 'draw_round_rect'); }); @@ -226,7 +242,8 @@ void main() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawDRRect(RRect.fromRectAndCorners(const Rect.fromLTRB(10, 20, 30, 40)), RRect.fromRectAndCorners(const Rect.fromLTRB(1, 2, 3, 4)), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(0, 0, 0, 0)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(0, 0, 0, 0)); await _checkScreenshot(rc, 'draw_drrect_empty'); }); @@ -236,34 +253,44 @@ void main() async { RRect.fromRectAndCorners(const Rect.fromLTRB(10, 20, 30, 40)), RRect.fromRectAndCorners(const Rect.fromLTRB(12, 22, 28, 38)), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(10, 20, 30, 40)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, 30, 40)); await _checkScreenshot(rc, 'draw_drrect'); }); test('Computes paint bounds for draw circle', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); + // Paint bounds of one circle. + RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawCircle(const Offset(20, 20), 10.0, testPaint); + rc.endRecording(); expect( - rc.computePaintBounds(), const Rect.fromLTRB(10.0, 10.0, 30.0, 30.0)); + rc.pictureBounds, const Rect.fromLTRB(10.0, 10.0, 30.0, 30.0)); + + // Paint bounds of a union of two circles. + rc = RecordingCanvas(screenRect); + rc.drawCircle(const Offset(20, 20), 10.0, testPaint); rc.drawCircle(const Offset(200, 300), 100.0, testPaint); + rc.endRecording(); expect( - rc.computePaintBounds(), const Rect.fromLTRB(10.0, 10.0, 300.0, 400.0)); + rc.pictureBounds, const Rect.fromLTRB(10.0, 10.0, 300.0, 400.0)); await _checkScreenshot(rc, 'draw_circle'); }); test('Computes paint bounds for draw image', () { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawImage(TestImage(), const Offset(50, 100), Paint()); + rc.endRecording(); expect( - rc.computePaintBounds(), const Rect.fromLTRB(50.0, 100.0, 70.0, 110.0)); + rc.pictureBounds, const Rect.fromLTRB(50.0, 100.0, 70.0, 110.0)); }); test('Computes paint bounds for draw image rect', () { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.drawImageRect(TestImage(), const Rect.fromLTRB(1, 1, 20, 10), const Rect.fromLTRB(5, 6, 400, 500), Paint()); + rc.endRecording(); expect( - rc.computePaintBounds(), const Rect.fromLTRB(5.0, 6.0, 400.0, 500.0)); + rc.pictureBounds, const Rect.fromLTRB(5.0, 6.0, 400.0, 500.0)); }); test('Computes paint bounds for single-line draw paragraph', () async { @@ -274,8 +301,9 @@ void main() async { const double widthConstraint = 300.0; paragraph.layout(const ParagraphConstraints(width: widthConstraint)); rc.drawParagraph(paragraph, const Offset(textLeft, textTop)); + rc.endRecording(); expect( - rc.computePaintBounds(), + rc.pictureBounds, const Rect.fromLTRB(textLeft, textTop, textLeft + widthConstraint, 21.0), ); await _checkScreenshot(rc, 'draw_paragraph'); @@ -286,27 +314,35 @@ void main() async { final Paragraph paragraph = createTestParagraph(); const double textLeft = 5.0; const double textTop = 7.0; - const double widthConstraint = - 130.0; // do not go lower than the shortest word. + // Do not go lower than the shortest word. + const double widthConstraint = 130.0; paragraph.layout(const ParagraphConstraints(width: widthConstraint)); rc.drawParagraph(paragraph, const Offset(textLeft, textTop)); + rc.endRecording(); expect( - rc.computePaintBounds(), + rc.pictureBounds, const Rect.fromLTRB(textLeft, textTop, textLeft + widthConstraint, 35.0), ); await _checkScreenshot(rc, 'draw_paragraph_multi_line'); }); test('Should exclude painting outside simple clipRect', () async { - final RecordingCanvas rc = RecordingCanvas(screenRect); + // One clipped line. + RecordingCanvas rc = RecordingCanvas(screenRect); rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100)); rc.drawLine(const Offset(10, 11), const Offset(20, 21), testPaint); + rc.endRecording(); + expect(rc.pictureBounds, Rect.zero); - expect(rc.computePaintBounds(), Rect.zero); + // Two clipped lines. + rc = RecordingCanvas(screenRect); + rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100)); + rc.drawLine(const Offset(10, 11), const Offset(20, 21), testPaint); rc.drawLine(const Offset(52, 53), const Offset(55, 56), testPaint); + rc.endRecording(); // Extra pixel due to default line length - expect(rc.computePaintBounds(), const Rect.fromLTRB(51, 52, 56, 57)); + expect(rc.pictureBounds, const Rect.fromLTRB(51, 52, 56, 57)); await _checkScreenshot(rc, 'clip_rect_simple'); }); @@ -314,13 +350,15 @@ void main() async { RecordingCanvas rc = RecordingCanvas(screenRect); rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100)); rc.drawRect(const Rect.fromLTRB(20, 60, 120, 70), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(50, 60, 100, 70)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(50, 60, 100, 70)); await _checkScreenshot(rc, 'clip_rect_intersects_paint_left_to_right'); rc = RecordingCanvas(screenRect); rc.clipRect(const Rect.fromLTRB(50, 50, 100, 100)); rc.drawRect(const Rect.fromLTRB(60, 20, 70, 200), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(60, 50, 70, 100)); + rc.endRecording(); + expect(rc.pictureBounds, const Rect.fromLTRB(60, 50, 70, 100)); await _checkScreenshot(rc, 'clip_rect_intersects_paint_top_to_bottom'); }); @@ -330,7 +368,9 @@ void main() async { rc.scale(2.0, 2.0); rc.clipRect(const Rect.fromLTRB(30, 30, 45, 45)); rc.drawRect(const Rect.fromLTRB(10, 30, 60, 35), testPaint); - expect(rc.computePaintBounds(), const Rect.fromLTRB(60, 60, 90, 70)); + rc.endRecording(); + + expect(rc.pictureBounds, const Rect.fromLTRB(60, 60, 90, 70)); await _checkScreenshot(rc, 'clip_rects_intersect'); }); @@ -340,8 +380,10 @@ void main() async { final Path path = Path(); path.addRect(const Rect.fromLTRB(20, 30, 100, 110)); rc.drawShadow(path, const Color(0xFFFF0000), 2.0, true); + rc.endRecording(); + expect( - rc.computePaintBounds(), + rc.pictureBounds, within(distance: 0.05, from: const Rect.fromLTRB(17.9, 28.5, 103.5, 114.1)), ); await _checkScreenshot(rc, 'path_with_shadow'); @@ -361,8 +403,10 @@ void main() async { ..scale(1, -1) ..clipRect(const Rect.fromLTRB(0, 0, 100, 50)) ..drawRect(const Rect.fromLTRB(0, 0, 100, 100), Paint()); + rc.endRecording(); + expect( - rc.computePaintBounds(), const Rect.fromLTRB(0.0, 50.0, 100.0, 100.0)); + rc.pictureBounds, const Rect.fromLTRB(0.0, 50.0, 100.0, 100.0)); await _checkScreenshot(rc, 'scale_negative'); }); @@ -374,8 +418,10 @@ void main() async { ..rotate(math.pi / 4.0) ..clipRect(const Rect.fromLTWH(-20, -20, 40, 40)) ..drawRect(const Rect.fromLTWH(-80, -80, 160, 160), Paint()); + rc.endRecording(); + expect( - rc.computePaintBounds(), + rc.pictureBounds, Rect.fromCircle(center: const Offset(50, 50), radius: 20 * math.sqrt(2)), ); await _checkScreenshot(rc, 'clip_rect_rotated'); @@ -388,8 +434,10 @@ void main() async { ..translate(50, 50) ..rotate(math.pi / 4.0) ..drawLine(const Offset(0, 0), const Offset(20, 20), Paint()); + rc.endRecording(); + expect( - rc.computePaintBounds(), + rc.pictureBounds, within(distance: 0.1, from: const Rect.fromLTRB(34.4, 48.6, 65.6, 79.7)), ); await _checkScreenshot(rc, 'line_rotated'); @@ -418,6 +466,7 @@ void main() async { ..style = PaintingStyle.stroke ..strokeWidth = 2.0 ..color = const Color(0xFF00FF00)); + rc.endRecording(); await _checkScreenshot(rc, 'reuse_path'); }); @@ -440,6 +489,7 @@ void main() async { ..strokeWidth = 2.0 ..color = const Color(0xFF404000)); rc.restore(); + rc.endRecording(); await _checkScreenshot(rc, 'path_with_line_and_roundrect'); }); @@ -536,7 +586,7 @@ void main() async { final SurfacePaint zeroSpreadPaint = SurfacePaint(); painter(canvas, zeroSpreadPaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.computePaintBounds())); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); sb.pop(); } @@ -550,7 +600,7 @@ void main() async { ..strokeWidth = 5.0; painter(canvas, thickStrokePaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.computePaintBounds())); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); sb.pop(); } @@ -563,7 +613,7 @@ void main() async { ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5.0); painter(canvas, maskFilterBlurPaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.computePaintBounds())); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); sb.pop(); } @@ -578,7 +628,7 @@ void main() async { ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 5.0); painter(canvas, thickStrokeAndBlurPaint); sb.addPicture(Offset.zero, recorder.endRecording()); - sb.addPicture(Offset.zero, drawBounds(canvas.computePaintBounds())); + sb.addPicture(Offset.zero, drawBounds(canvas.pictureBounds)); sb.pop(); } diff --git a/lib/web_ui/test/mock_engine_canvas.dart b/lib/web_ui/test/mock_engine_canvas.dart index 81f17bab46236..b0253225dc7e8 100644 --- a/lib/web_ui/test/mock_engine_canvas.dart +++ b/lib/web_ui/test/mock_engine_canvas.dart @@ -137,7 +137,10 @@ class MockEngineCanvas implements EngineCanvas { @override void drawRect(Rect rect, SurfacePaintData paint) { - _called('drawRect', arguments: paint); + _called('drawRect', arguments: { + 'rect': rect, + 'paint': paint, + }); } @override diff --git a/shell/common/canvas_spy.cc b/shell/common/canvas_spy.cc index 5b62a34953a23..4b6301690c599 100644 --- a/shell/common/canvas_spy.cc +++ b/shell/common/canvas_spy.cc @@ -53,7 +53,11 @@ void DidDrawCanvas::willRestore() {} void DidDrawCanvas::didConcat(const SkMatrix& matrix) {} +#ifdef SK_SUPPORT_LEGACY_DIDCONCAT44 void DidDrawCanvas::didConcat44(const SkScalar[]) {} +#else +void DidDrawCanvas::didConcat44(const SkM44&) {} +#endif void DidDrawCanvas::didScale(SkScalar, SkScalar) {} diff --git a/shell/common/canvas_spy.h b/shell/common/canvas_spy.h index d5c75be34df40..594cd5224f038 100644 --- a/shell/common/canvas_spy.h +++ b/shell/common/canvas_spy.h @@ -71,7 +71,11 @@ class DidDrawCanvas final : public SkCanvasVirtualEnforcer { // |SkCanvasVirtualEnforcer| void didConcat(const SkMatrix&) override; +#ifdef SK_SUPPORT_LEGACY_DIDCONCAT44 void didConcat44(const SkScalar[]) override; +#else + void didConcat44(const SkM44&) override; +#endif void didScale(SkScalar, SkScalar) override; void didTranslate(SkScalar, SkScalar) override; diff --git a/shell/platform/BUILD.gn b/shell/platform/BUILD.gn index 925177e67472b..d84a2ebbc5d4c 100644 --- a/shell/platform/BUILD.gn +++ b/shell/platform/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/fuchsia/sdk.gni") +import("//flutter/shell/platform/config.gni") group("platform") { if (is_mac || is_ios) { @@ -14,18 +15,15 @@ group("platform") { "android", ] } else if (is_linux) { - if (is_fuchsia_host) { - # The linux build is not supported as part of the Fuchsia host build. - deps = [] - } else { - deps = [ - "linux", - ] + deps = [] + if (enable_desktop_embeddings) { + deps += [ "linux" ] } } else if (is_win) { - deps = [ - "windows", - ] + deps = [] + if (enable_desktop_embeddings) { + deps += [ "windows" ] + } } else if (is_fuchsia) { deps = [ "fuchsia", diff --git a/shell/platform/android/BUILD.gn b/shell/platform/android/BUILD.gn index 57992c62961c2..cc5d0f8b59713 100644 --- a/shell/platform/android/BUILD.gn +++ b/shell/platform/android/BUILD.gn @@ -446,6 +446,7 @@ action("robolectric_tests") { "test/io/flutter/plugins/GeneratedPluginRegistrant.java", "test/io/flutter/util/FakeKeyEvent.java", "test/io/flutter/util/PreconditionsTest.java", + "test/io/flutter/view/AccessibilityBridgeTest.java", ] outputs = [ diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 31cbeb9673484..33e50f295ae05 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -14,12 +14,15 @@ import android.os.LocaleList; import android.text.format.DateFormat; import android.util.AttributeSet; +import android.util.SparseArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import android.view.ViewStructure; import android.view.WindowInsets; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeProvider; +import android.view.autofill.AutofillValue; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.widget.FrameLayout; @@ -283,6 +286,9 @@ private void init() { // FlutterView needs to be focusable so that the InputMethodManager can interact with it. setFocusable(true); setFocusableInTouchMode(true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS); + } } /** @@ -898,6 +904,17 @@ private void sendViewportMetricsToFlutter() { flutterEngine.getRenderer().setViewportMetrics(viewportMetrics); } + @Override + public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { + super.onProvideAutofillVirtualStructure(structure, flags); + textInputPlugin.onProvideAutofillVirtualStructure(structure, flags); + } + + @Override + public void autofill(SparseArray values) { + textInputPlugin.autofill(values); + } + /** * Render modes for a {@link FlutterView}. * diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java index 80d2e419c8fce..a8fe0d5f95784 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/TextInputChannel.java @@ -1,5 +1,7 @@ package io.flutter.embedding.engine.systemchannels; +import android.os.Build; +import android.view.View; import android.view.inputmethod.EditorInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -10,6 +12,7 @@ import io.flutter.plugin.common.MethodChannel; import java.util.Arrays; import java.util.HashMap; +import java.util.Map; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -85,6 +88,22 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result.error("error", exception.getMessage(), null); } break; + case "TextInput.setEditableSizeAndTransform": + try { + final JSONObject arguments = (JSONObject) args; + final double width = arguments.getDouble("width"); + final double height = arguments.getDouble("height"); + final JSONArray jsonMatrix = arguments.getJSONArray("transform"); + final double[] matrix = new double[16]; + for (int i = 0; i < 16; i++) { + matrix[i] = jsonMatrix.getDouble(i); + } + + textInputMethodHandler.setEditableSizeAndTransform(width, height, matrix); + } catch (JSONException exception) { + result.error("error", exception.getMessage(), null); + } + break; case "TextInput.clearClient": textInputMethodHandler.clearClient(); result.success(null); @@ -119,6 +138,16 @@ public void requestExistingInputState() { channel.invokeMethod("TextInputClient.requestExistingInputState", null); } + private static HashMap createEditingStateJSON( + String text, int selectionStart, int selectionEnd, int composingStart, int composingEnd) { + HashMap state = new HashMap<>(); + state.put("text", text); + state.put("selectionBase", selectionStart); + state.put("selectionExtent", selectionEnd); + state.put("composingBase", composingStart); + state.put("composingExtent", composingEnd); + return state; + } /** * Instructs Flutter to update its text input editing state to reflect the given configuration. */ @@ -147,16 +176,31 @@ public void updateEditingState( + "Composing end: " + composingEnd); - HashMap state = new HashMap<>(); - state.put("text", text); - state.put("selectionBase", selectionStart); - state.put("selectionExtent", selectionEnd); - state.put("composingBase", composingStart); - state.put("composingExtent", composingEnd); + final HashMap state = + createEditingStateJSON(text, selectionStart, selectionEnd, composingStart, composingEnd); channel.invokeMethod("TextInputClient.updateEditingState", Arrays.asList(inputClientId, state)); } + public void updateEditingStateWithTag( + int inputClientId, HashMap editStates) { + Log.v( + TAG, + "Sending message to update editing state for " + + String.valueOf(editStates.size()) + + " field(s)."); + + final HashMap> json = new HashMap<>(); + for (Map.Entry element : editStates.entrySet()) { + final TextEditState state = element.getValue(); + json.put( + element.getKey(), + createEditingStateJSON(state.text, state.selectionStart, state.selectionEnd, -1, -1)); + } + channel.invokeMethod( + "TextInputClient.updateEditingStateWithTag", Arrays.asList(inputClientId, json)); + } + /** Instructs Flutter to execute a "newline" action. */ public void newline(int inputClientId) { Log.v(TAG, "Sending 'newline' message."); @@ -229,6 +273,13 @@ public interface TextInputMethodHandler { // TODO(mattcarroll): javadoc void hide(); + /** + * Requests that the autofill dropdown menu appear for the current client. + * + *

Has no effect if the current client does not support autofill. + */ + void requestAutofill(); + // TODO(mattcarroll): javadoc void setClient(int textInputClientId, @NonNull Configuration configuration); @@ -242,6 +293,16 @@ public interface TextInputMethodHandler { */ void setPlatformViewClient(int id); + /** + * Sets the size and the transform matrix of the current text input client. + * + * @param width the width of text input client. Must be finite. + * @param height the height of text input client. Must be finite. + * @param transform a 4x4 matrix that maps the local paint coordinate system to coordinate + * system of the FlutterView that owns the current client. + */ + void setEditableSizeAndTransform(double width, double height, double[] transform); + // TODO(mattcarroll): javadoc void setEditingState(@NonNull TextEditState editingState); @@ -257,7 +318,14 @@ public static Configuration fromJson(@NonNull JSONObject json) if (inputActionName == null) { throw new JSONException("Configuration JSON missing 'inputAction' property."); } - + Configuration[] fields = null; + if (!json.isNull("fields")) { + final JSONArray jsonFields = json.getJSONArray("fields"); + fields = new Configuration[jsonFields.length()]; + for (int i = 0; i < fields.length; i++) { + fields[i] = Configuration.fromJson(jsonFields.getJSONObject(i)); + } + } final Integer inputAction = inputActionFromTextInputAction(inputActionName); return new Configuration( json.optBoolean("obscureText"), @@ -266,7 +334,9 @@ public static Configuration fromJson(@NonNull JSONObject json) TextCapitalization.fromValue(json.getString("textCapitalization")), InputType.fromJson(json.getJSONObject("inputType")), inputAction, - json.isNull("actionLabel") ? null : json.getString("actionLabel")); + json.isNull("actionLabel") ? null : json.getString("actionLabel"), + json.isNull("autofill") ? null : Autofill.fromJson(json.getJSONObject("autofill")), + fields); } @NonNull @@ -296,6 +366,117 @@ private static Integer inputActionFromTextInputAction(@NonNull String inputActio } } + public static class Autofill { + public static Autofill fromJson(@NonNull JSONObject json) + throws JSONException, NoSuchFieldException { + final String uniqueIdentifier = json.getString("uniqueIdentifier"); + final JSONArray hints = json.getJSONArray("hints"); + final JSONObject editingState = json.getJSONObject("editingValue"); + final String[] hintList = new String[hints.length()]; + + for (int i = 0; i < hintList.length; i++) { + hintList[i] = translateAutofillHint(hints.getString(i)); + } + return new Autofill(uniqueIdentifier, hintList, TextEditState.fromJson(editingState)); + } + + public final String uniqueIdentifier; + public final String[] hints; + public final TextEditState editState; + + @NonNull + private static String translateAutofillHint(@NonNull String hint) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return hint; + } + switch (hint) { + case "addressCity": + return "addressLocality"; + case "addressState": + return "addressRegion"; + case "birthday": + return "birthDateFull"; + case "birthdayDay": + return "birthDateDay"; + case "birthdayMonth": + return "birthDateMonth"; + case "birthdayYear": + return "birthDateYear"; + case "countryName": + return "addressCountry"; + case "creditCardExpirationDate": + return View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE; + case "creditCardExpirationDay": + return View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY; + case "creditCardExpirationMonth": + return View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH; + case "creditCardExpirationYear": + return View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR; + case "creditCardNumber": + return View.AUTOFILL_HINT_CREDIT_CARD_NUMBER; + case "creditCardSecurityCode": + return View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE; + case "email": + return View.AUTOFILL_HINT_EMAIL_ADDRESS; + case "familyName": + return "personFamilyName"; + case "fullStreetAddress": + return "streetAddress"; + case "gender": + return "gender"; + case "givenName": + return "personGivenName"; + case "middleInitial": + return "personMiddleInitial"; + case "middleName": + return "personMiddleName"; + case "name": + return "personName"; + case "namePrefix": + return "personNamePrefix"; + case "nameSuffix": + return "personNameSuffix"; + case "newPassword": + return "newPassword"; + case "newUsername": + return "newUsername"; + case "oneTimeCode": + return "smsOTPCode"; + case "password": + return View.AUTOFILL_HINT_PASSWORD; + case "postalAddress": + return View.AUTOFILL_HINT_POSTAL_ADDRESS; + case "postalAddressExtended": + return "extendedAddress"; + case "postalAddressExtendedPostalCode": + return "extendedPostalCode"; + case "postalCode": + return View.AUTOFILL_HINT_POSTAL_CODE; + case "telephoneNumber": + return "phoneNumber"; + case "telephoneNumberCountryCode": + return "phoneCountryCode"; + case "telephoneNumberDevice": + return "phoneNumberDevice"; + case "telephoneNumberNational": + return "phoneNational"; + case "username": + return View.AUTOFILL_HINT_USERNAME; + default: + return hint; + } + } + + public Autofill( + @NonNull String uniqueIdentifier, + @NonNull String[] hints, + @NonNull TextEditState editingState) { + this.uniqueIdentifier = uniqueIdentifier; + this.hints = hints; + this.editState = editingState; + } + } + public final boolean obscureText; public final boolean autocorrect; public final boolean enableSuggestions; @@ -303,6 +484,8 @@ private static Integer inputActionFromTextInputAction(@NonNull String inputActio @NonNull public final InputType inputType; @Nullable public final Integer inputAction; @Nullable public final String actionLabel; + @Nullable public final Autofill autofill; + @Nullable public final Configuration[] fields; public Configuration( boolean obscureText, @@ -311,7 +494,9 @@ public Configuration( @NonNull TextCapitalization textCapitalization, @NonNull InputType inputType, @Nullable Integer inputAction, - @Nullable String actionLabel) { + @Nullable String actionLabel, + @Nullable Autofill autofill, + @Nullable Configuration[] fields) { this.obscureText = obscureText; this.autocorrect = autocorrect; this.enableSuggestions = enableSuggestions; @@ -319,6 +504,8 @@ public Configuration( this.inputType = inputType; this.inputAction = inputAction; this.actionLabel = actionLabel; + this.autofill = autofill; + this.fields = fields; } } diff --git a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java index d1b58039fb910..a990ff386108e 100644 --- a/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java +++ b/shell/platform/android/io/flutter/plugin/editing/InputConnectionAdaptor.java @@ -16,6 +16,7 @@ import android.text.Layout; import android.text.Selection; import android.text.TextPaint; +import android.text.method.TextKeyListener; import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.BaseInputConnection; @@ -321,35 +322,11 @@ public boolean sendKeyEvent(KeyEvent event) { updateEditingState(); return true; } else if (selStart > 0) { - // Delete to the left/right of the cursor depending on direction of text. - // TODO(garyq): Explore how to obtain per-character direction. The - // isRTLCharAt() call below is returning blanket direction assumption - // based on the first character in the line. - boolean isRtl = mLayout.isRtlCharAt(mLayout.getLineForOffset(selStart)); - try { - if (isRtl) { - Selection.extendRight(mEditable, mLayout); - } else { - Selection.extendLeft(mEditable, mLayout); - } - } catch (IndexOutOfBoundsException e) { - // On some Chinese devices (primarily Huawei, some Xiaomi), - // on initial app startup before focus is lost, the - // Selection.extendLeft and extendRight calls always extend - // from the index of the initial contents of mEditable. This - // try-catch will prevent crashing on Huawei devices by falling - // back to a simple way of deletion, although this a hack and - // will not handle emojis. - Selection.setSelection(mEditable, selStart, selStart - 1); + if (TextKeyListener.getInstance().onKeyDown(null, mEditable, event.getKeyCode(), event)) { + updateEditingState(); + return true; } - int newStart = clampIndexToEditable(Selection.getSelectionStart(mEditable), mEditable); - int newEnd = clampIndexToEditable(Selection.getSelectionEnd(mEditable), mEditable); - Selection.setSelection(mEditable, Math.min(newStart, newEnd)); - // Min/Max the values since RTL selections will start at a higher - // index than they end at. - mEditable.delete(Math.min(newStart, newEnd), Math.max(newStart, newEnd)); - updateEditingState(); - return true; + return false; } } else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) { int selStart = Selection.getSelectionStart(mEditable); diff --git a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java index 17ccea06728de..b14ac4112f611 100644 --- a/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java +++ b/shell/platform/android/io/flutter/plugin/editing/TextInputPlugin.java @@ -6,12 +6,18 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.graphics.Rect; import android.os.Build; import android.provider.Settings; import android.text.Editable; import android.text.InputType; import android.text.Selection; +import android.util.SparseArray; import android.view.View; +import android.view.ViewStructure; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillManager; +import android.view.autofill.AutofillValue; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -23,18 +29,22 @@ import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.systemchannels.TextInputChannel; import io.flutter.plugin.platform.PlatformViewsController; +import java.util.HashMap; /** Android implementation of the text input plugin. */ public class TextInputPlugin { @NonNull private final View mView; @NonNull private final InputMethodManager mImm; + @NonNull private final AutofillManager afm; @NonNull private final TextInputChannel textInputChannel; @NonNull private InputTarget inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0); @Nullable private TextInputChannel.Configuration configuration; + @Nullable private SparseArray mAutofillConfigurations; @Nullable private Editable mEditable; private boolean mRestartInputPending; @Nullable private InputConnection lastInputConnection; @NonNull private PlatformViewsController platformViewsController; + @Nullable private Rect lastClientRect; private final boolean restartAlwaysRequired; // When true following calls to createInputConnection will return the cached lastInputConnection @@ -49,6 +59,11 @@ public TextInputPlugin( @NonNull PlatformViewsController platformViewsController) { mView = view; mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + afm = view.getContext().getSystemService(AutofillManager.class); + } else { + afm = null; + } textInputChannel = new TextInputChannel(dartExecutor); textInputChannel.setTextInputMethodHandler( @@ -63,6 +78,11 @@ public void hide() { hideTextInput(mView); } + @Override + public void requestAutofill() { + notifyViewEntered(); + } + @Override public void setClient( int textInputClientId, TextInputChannel.Configuration configuration) { @@ -79,6 +99,11 @@ public void setEditingState(TextInputChannel.TextEditState editingState) { setTextInputEditingState(mView, editingState); } + @Override + public void setEditableSizeAndTransform(double width, double height, double[] transform) { + saveEditableSizeAndTransform(width, height, transform); + } + @Override public void clearClient() { clearTextInputClient(); @@ -268,6 +293,7 @@ private void showTextInput(View view) { } private void hideTextInput(View view) { + notifyViewExited(); // Note: a race condition may lead to us hiding the keyboard here just after a platform view has // shown it. // This can only potentially happen when switching focus from a Flutter text field to a platform @@ -277,16 +303,51 @@ private void hideTextInput(View view) { mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0); } + private void notifyViewEntered() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || afm == null || !needsAutofill()) { + return; + } + + final String triggerIdentifier = configuration.autofill.uniqueIdentifier; + final int[] offset = new int[2]; + mView.getLocationOnScreen(offset); + Rect rect = new Rect(lastClientRect); + rect.offset(offset[0], offset[1]); + afm.notifyViewEntered(mView, triggerIdentifier.hashCode(), rect); + } + + private void notifyViewExited() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O + || afm == null + || configuration == null + || configuration.autofill == null) { + return; + } + + final String triggerIdentifier = configuration.autofill.uniqueIdentifier; + afm.notifyViewExited(mView, triggerIdentifier.hashCode()); + } + + private void notifyValueChanged(String newValue) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || afm == null || !needsAutofill()) { + return; + } + + final String triggerIdentifier = configuration.autofill.uniqueIdentifier; + afm.notifyValueChanged(mView, triggerIdentifier.hashCode(), AutofillValue.forText(newValue)); + } + @VisibleForTesting void setTextInputClient(int client, TextInputChannel.Configuration configuration) { inputTarget = new InputTarget(InputTarget.Type.FRAMEWORK_CLIENT, client); - this.configuration = configuration; + updateAutofillConfigurationIfNeeded(configuration); mEditable = Editable.Factory.getInstance().newEditable(""); // setTextInputClient will be followed by a call to setTextInputEditingState. // Do a restartInput at that time. mRestartInputPending = true; unlockPlatformViewInputConnection(); + lastClientRect = null; } private void setPlatformViewTextInputClient(int platformViewId) { @@ -320,6 +381,7 @@ void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { if (!state.text.equals(mEditable.toString())) { mEditable.replace(0, mEditable.length(), state.text); } + notifyValueChanged(mEditable.toString()); // Always apply state to selection which handles updating the selection if needed. applyStateToSelection(state); InputConnection connection = getLastInputConnection(); @@ -342,6 +404,141 @@ void setTextInputEditingState(View view, TextInputChannel.TextEditState state) { } } + private interface MinMax { + void inspect(double x, double y); + } + + private void saveEditableSizeAndTransform(double width, double height, double[] matrix) { + final double[] minMax = new double[4]; // minX, maxX, minY, maxY. + final boolean isAffine = matrix[3] == 0 && matrix[7] == 0 && matrix[15] == 1; + minMax[0] = minMax[1] = matrix[12] / matrix[15]; // minX and maxX. + minMax[2] = minMax[3] = matrix[13] / matrix[15]; // minY and maxY. + + final MinMax finder = + new MinMax() { + @Override + public void inspect(double x, double y) { + final double w = isAffine ? 1 : 1 / (matrix[3] * x + matrix[7] * y + matrix[15]); + final double tx = (matrix[0] * x + matrix[4] * y + matrix[12]) * w; + final double ty = (matrix[1] * x + matrix[5] * y + matrix[13]) * w; + + if (tx < minMax[0]) { + minMax[0] = tx; + } else if (tx > minMax[1]) { + minMax[1] = tx; + } + + if (ty < minMax[2]) { + minMax[2] = ty; + } else if (ty > minMax[3]) { + minMax[3] = ty; + } + } + }; + + finder.inspect(width, 0); + finder.inspect(width, height); + finder.inspect(0, height); + final Float density = mView.getContext().getResources().getDisplayMetrics().density; + lastClientRect = + new Rect( + (int) (minMax[0] * density), + (int) (minMax[2] * density), + (int) Math.ceil(minMax[1] * density), + (int) Math.ceil(minMax[3] * density)); + } + + private void updateAutofillConfigurationIfNeeded(TextInputChannel.Configuration configuration) { + notifyViewExited(); + this.configuration = configuration; + final TextInputChannel.Configuration[] configurations = configuration.fields; + + if (configuration.autofill == null) { + // Disables autofill if the configuration doesn't have an autofill field. + mAutofillConfigurations = null; + return; + } + + mAutofillConfigurations = new SparseArray<>(); + + if (configurations == null) { + mAutofillConfigurations.put( + configuration.autofill.uniqueIdentifier.hashCode(), configuration); + } else { + for (TextInputChannel.Configuration config : configurations) { + TextInputChannel.Configuration.Autofill autofill = config.autofill; + if (autofill == null) { + continue; + } + + mAutofillConfigurations.put(autofill.uniqueIdentifier.hashCode(), config); + } + } + } + + private boolean needsAutofill() { + return mAutofillConfigurations != null; + } + + public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !needsAutofill()) { + return; + } + + final String triggerIdentifier = configuration.autofill.uniqueIdentifier; + final AutofillId parentId = structure.getAutofillId(); + for (int i = 0; i < mAutofillConfigurations.size(); i++) { + final int autofillId = mAutofillConfigurations.keyAt(i); + final TextInputChannel.Configuration config = mAutofillConfigurations.valueAt(i); + final TextInputChannel.Configuration.Autofill autofill = config.autofill; + if (autofill == null) { + continue; + } + + structure.addChildCount(1); + final ViewStructure child = structure.newChild(i); + child.setAutofillId(parentId, autofillId); + child.setAutofillValue(AutofillValue.forText(autofill.editState.text)); + child.setAutofillHints(autofill.hints); + child.setAutofillType(View.AUTOFILL_TYPE_TEXT); + child.setVisibility(View.VISIBLE); + } + } + + public void autofill(SparseArray values) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + + final TextInputChannel.Configuration.Autofill currentAutofill = configuration.autofill; + if (currentAutofill == null) { + return; + } + + final HashMap editingValues = new HashMap<>(); + for (int i = 0; i < values.size(); i++) { + int virtualId = values.keyAt(i); + + final TextInputChannel.Configuration config = mAutofillConfigurations.get(virtualId); + if (config == null || config.autofill == null) { + continue; + } + + final TextInputChannel.Configuration.Autofill autofill = config.autofill; + final String value = values.valueAt(i).getTextValue().toString(); + final TextInputChannel.TextEditState newState = + new TextInputChannel.TextEditState(value, value.length(), value.length()); + + // The value of the currently focused text field needs to be updated. + if (autofill.uniqueIdentifier.equals(currentAutofill.uniqueIdentifier)) { + setTextInputEditingState(mView, newState); + } + editingValues.put(autofill.uniqueIdentifier, newState); + } + + textInputChannel.updateEditingStateWithTag(inputTarget.id, editingValues); + } + // Samsung's Korean keyboard has a bug where it always attempts to combine characters based on // its internal state, ignoring if and when the cursor is moved programmatically. The same bug // also causes non-korean keyboards to occasionally duplicate text when tapping in the middle @@ -394,6 +591,8 @@ private void clearTextInputClient() { } inputTarget = new InputTarget(InputTarget.Type.NO_TARGET, 0); unlockPlatformViewInputConnection(); + notifyViewExited(); + lastClientRect = null; } private static class InputTarget { diff --git a/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 4d4fe46a33281..fa1050e0c7f96 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -26,6 +26,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import androidx.annotation.VisibleForTesting; import io.flutter.BuildConfig; import io.flutter.embedding.engine.systemchannels.AccessibilityChannel; import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate; @@ -333,10 +334,32 @@ public AccessibilityBridge( // TODO(mattcarrol): Add the annotation once the plumbing is done. // https://github.com/flutter/flutter/issues/29618 PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate) { + this( + rootAccessibilityView, + accessibilityChannel, + accessibilityManager, + contentResolver, + new AccessibilityViewEmbedder(rootAccessibilityView, MIN_ENGINE_GENERATED_NODE_ID), + platformViewsAccessibilityDelegate); + } + + @VisibleForTesting + public AccessibilityBridge( + @NonNull View rootAccessibilityView, + @NonNull AccessibilityChannel accessibilityChannel, + @NonNull AccessibilityManager accessibilityManager, + @NonNull ContentResolver contentResolver, + @NonNull AccessibilityViewEmbedder accessibilityViewEmbedder, + // This should be @NonNull once the plumbing for + // io.flutter.embedding.engine.android.FlutterView is done. + // TODO(mattcarrol): Add the annotation once the plumbing is done. + // https://github.com/flutter/flutter/issues/29618 + PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate) { this.rootAccessibilityView = rootAccessibilityView; this.accessibilityChannel = accessibilityChannel; this.accessibilityManager = accessibilityManager; this.contentResolver = contentResolver; + this.accessibilityViewEmbedder = accessibilityViewEmbedder; this.platformViewsAccessibilityDelegate = platformViewsAccessibilityDelegate; // Tell Flutter whether accessibility is initially active or not. Then register a listener @@ -388,8 +411,6 @@ public void onTouchExplorationStateChanged(boolean isTouchExplorationEnabled) { if (platformViewsAccessibilityDelegate != null) { platformViewsAccessibilityDelegate.attachAccessibilityBridge(this); } - accessibilityViewEmbedder = - new AccessibilityViewEmbedder(rootAccessibilityView, MIN_ENGINE_GENERATED_NODE_ID); } /** @@ -739,6 +760,12 @@ && shouldSetCollectionInfo(semanticsNode)) { result.setLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE); } + if (semanticsNode.hasFlag(Flag.IS_TEXT_FIELD)) { + result.setText(semanticsNode.getValueLabelHint()); + } else if (!semanticsNode.hasFlag(Flag.SCOPES_ROUTE)) { + result.setContentDescription(semanticsNode.getValueLabelHint()); + } + boolean hasCheckedState = semanticsNode.hasFlag(Flag.HAS_CHECKED_STATE); boolean hasToggledState = semanticsNode.hasFlag(Flag.HAS_TOGGLED_STATE); if (BuildConfig.DEBUG && (hasCheckedState && hasToggledState)) { @@ -747,7 +774,6 @@ && shouldSetCollectionInfo(semanticsNode)) { result.setCheckable(hasCheckedState || hasToggledState); if (hasCheckedState) { result.setChecked(semanticsNode.hasFlag(Flag.IS_CHECKED)); - result.setContentDescription(semanticsNode.getValueLabelHint()); if (semanticsNode.hasFlag(Flag.IS_IN_MUTUALLY_EXCLUSIVE_GROUP)) { result.setClassName("android.widget.RadioButton"); } else { @@ -756,13 +782,7 @@ && shouldSetCollectionInfo(semanticsNode)) { } else if (hasToggledState) { result.setChecked(semanticsNode.hasFlag(Flag.IS_TOGGLED)); result.setClassName("android.widget.Switch"); - result.setContentDescription(semanticsNode.getValueLabelHint()); - } else if (!semanticsNode.hasFlag(Flag.SCOPES_ROUTE)) { - // Setting the text directly instead of the content description - // will replace the "checked" or "not-checked" label. - result.setText(semanticsNode.getValueLabelHint()); } - result.setSelected(semanticsNode.hasFlag(Flag.IS_SELECTED)); // Heading support @@ -1581,15 +1601,31 @@ private void willRemoveSemanticsNode(SemanticsNode semanticsNodeToBeRemoved) { // for null'ing accessibilityFocusedSemanticsNode, inputFocusedSemanticsNode, // and hoveredObject. Is this a hook method or a command? semanticsNodeToBeRemoved.parent = null; + + if (semanticsNodeToBeRemoved.platformViewId != -1 + && embeddedAccessibilityFocusedNodeId != null + && accessibilityViewEmbedder.platformViewOfNode(embeddedAccessibilityFocusedNodeId) + == platformViewsAccessibilityDelegate.getPlatformViewById( + semanticsNodeToBeRemoved.platformViewId)) { + // If the currently focused a11y node is within a platform view that is + // getting removed: clear it's a11y focus. + sendAccessibilityEvent( + embeddedAccessibilityFocusedNodeId, + AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); + embeddedAccessibilityFocusedNodeId = null; + } + if (accessibilityFocusedSemanticsNode == semanticsNodeToBeRemoved) { sendAccessibilityEvent( accessibilityFocusedSemanticsNode.id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); accessibilityFocusedSemanticsNode = null; } + if (inputFocusedSemanticsNode == semanticsNodeToBeRemoved) { inputFocusedSemanticsNode = null; } + if (hoveredObject == semanticsNodeToBeRemoved) { hoveredObject = null; } @@ -1660,7 +1696,7 @@ public enum Action { // Must match SemanticsFlag in semantics.dart // https://github.com/flutter/engine/blob/master/lib/ui/semantics.dart - private enum Flag { + /* Package */ enum Flag { HAS_CHECKED_STATE(1 << 0), IS_CHECKED(1 << 1), IS_SELECTED(1 << 2), diff --git a/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java b/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java index 0fb2957d92410..2823eb795d17b 100644 --- a/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java +++ b/shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java @@ -44,7 +44,7 @@ * corresponding platform view and `originId`. */ @Keep -final class AccessibilityViewEmbedder { +class AccessibilityViewEmbedder { private static final String TAG = "AccessibilityBridge"; private final ReflectionAccessors reflectionAccessors; @@ -387,6 +387,18 @@ public boolean onAccessibilityHoverEvent(int rootFlutterId, @NonNull MotionEvent return origin.view.dispatchGenericMotionEvent(translatedEvent); } + /** + * Returns the View that contains the accessibility node identified by the provided flutterId or + * null if it doesn't belong to a view. + */ + public View platformViewOfNode(int flutterId) { + ViewAndId viewAndId = flutterIdToOrigin.get(flutterId); + if (viewAndId == null) { + return null; + } + return viewAndId.view; + } + private static class ViewAndId { final View view; final int id; diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 23362b64a3499..40fcea0aceec5 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -21,15 +21,18 @@ import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; +import android.util.SparseArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; +import android.view.ViewStructure; import android.view.WindowInsets; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeProvider; +import android.view.autofill.AutofillValue; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; @@ -445,6 +448,17 @@ public boolean checkInputConnectionProxy(View view) { .checkInputConnectionProxy(view); } + @Override + public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { + super.onProvideAutofillVirtualStructure(structure, flags); + mTextInputPlugin.onProvideAutofillVirtualStructure(structure, flags); + } + + @Override + public void autofill(SparseArray values) { + mTextInputPlugin.autofill(values); + } + @Override public boolean onTouchEvent(MotionEvent event) { if (!isAttached()) { diff --git a/shell/platform/android/platform_view_android.cc b/shell/platform/android/platform_view_android.cc index a58efec690e37..d5e53d4f1bf42 100644 --- a/shell/platform/android/platform_view_android.cc +++ b/shell/platform/android/platform_view_android.cc @@ -247,6 +247,14 @@ void PlatformViewAndroid::UpdateSemantics( value.second.customAccessibilityActions.size() * kBytesPerChild; } + // The encoding defined here is used in: + // + // * AccessibilityBridge.java + // * AccessibilityBridgeTest.java + // * accessibility_bridge.mm + // + // If any of the encoding structure or length is changed, those locations + // must be updated (at a minimum). std::vector buffer(num_bytes); int32_t* buffer_int32 = reinterpret_cast(&buffer[0]); float* buffer_float32 = reinterpret_cast(&buffer[0]); diff --git a/shell/platform/android/test/io/flutter/FlutterTestSuite.java b/shell/platform/android/test/io/flutter/FlutterTestSuite.java index d793f5a731877..509c91c298988 100644 --- a/shell/platform/android/test/io/flutter/FlutterTestSuite.java +++ b/shell/platform/android/test/io/flutter/FlutterTestSuite.java @@ -22,6 +22,7 @@ import io.flutter.plugin.platform.PlatformPluginTest; import io.flutter.plugin.platform.SingleViewPresentationTest; import io.flutter.util.PreconditionsTest; +import io.flutter.view.AccessibilityBridgeTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @@ -55,6 +56,7 @@ SingleViewPresentationTest.class, SmokeTest.class, TextInputPluginTest.class, + AccessibilityBridgeTest.class, }) /** Runs all of the unit tests listed in the {@code @SuiteClasses} annotation. */ public class FlutterTestSuite {} diff --git a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java index bf0f513a0835e..bbca2464a6232 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/InputConnectionAdaptorTest.java @@ -313,15 +313,44 @@ public void inputConnectionAdaptor_RepeatFilter() throws NullPointerException { assertEquals(textInputChannel.selectionEnd, 4); } + @Test + public void testSendKeyEvent_delKeyDeletesBackward() { + int selStart = 29; + Editable editable = sampleRtlEditable(selStart, selStart); + InputConnectionAdaptor adaptor = sampleInputConnectionAdaptor(editable); + + KeyEvent downKeyDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL); + + for (int i = 0; i < 9; i++) { + boolean didConsume = adaptor.sendKeyEvent(downKeyDown); + assertTrue(didConsume); + } + assertEquals(Selection.getSelectionStart(editable), 19); + + for (int i = 0; i < 9; i++) { + boolean didConsume = adaptor.sendKeyEvent(downKeyDown); + assertTrue(didConsume); + } + assertEquals(Selection.getSelectionStart(editable), 10); + } + private static final String SAMPLE_TEXT = "Lorem ipsum dolor sit amet," + "\nconsectetur adipiscing elit."; + private static final String SAMPLE_RTL_TEXT = "متن ساختگی" + "\nبرای تستfor test😊"; + private static Editable sampleEditable(int selStart, int selEnd) { SpannableStringBuilder sample = new SpannableStringBuilder(SAMPLE_TEXT); Selection.setSelection(sample, selStart, selEnd); return sample; } + private static Editable sampleRtlEditable(int selStart, int selEnd) { + SpannableStringBuilder sample = new SpannableStringBuilder(SAMPLE_RTL_TEXT); + Selection.setSelection(sample, selStart, selEnd); + return sample; + } + private static InputConnectionAdaptor sampleInputConnectionAdaptor(Editable editable) { View testView = new View(RuntimeEnvironment.application); int client = 0; 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 b2ecfd7b13fd6..53fb4297875e8 100644 --- a/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/editing/TextInputPluginTest.java @@ -2,11 +2,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.AssetManager; @@ -15,11 +19,13 @@ import android.util.SparseIntArray; import android.view.KeyEvent; import android.view.View; +import android.view.ViewStructure; import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; +import io.flutter.embedding.android.FlutterView; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.engine.dart.DartExecutor; import io.flutter.embedding.engine.systemchannels.TextInputChannel; @@ -103,7 +109,15 @@ public void setTextInputEditingState_doesNotRestartWhenTextIsIdentical() { textInputPlugin.setTextInputClient( 0, new TextInputChannel.Configuration( - false, false, true, TextInputChannel.TextCapitalization.NONE, null, null, null)); + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + null, + null)); // There's a pending restart since we initialized the text input client. Flush that now. textInputPlugin.setTextInputEditingState( testView, new TextInputChannel.TextEditState("", 0, 0)); @@ -132,7 +146,15 @@ public void setTextInputEditingState_alwaysSetEditableWhenDifferent() { textInputPlugin.setTextInputClient( 0, new TextInputChannel.Configuration( - false, false, true, TextInputChannel.TextCapitalization.NONE, null, null, null)); + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + null, + null)); // There's a pending restart since we initialized the text input client. Flush that now. With // changed text, we should // always set the Editable contents. @@ -173,7 +195,15 @@ public void setTextInputEditingState_alwaysRestartsOnAffectedDevices2() { textInputPlugin.setTextInputClient( 0, new TextInputChannel.Configuration( - false, false, true, TextInputChannel.TextCapitalization.NONE, null, null, null)); + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + null, + null)); // There's a pending restart since we initialized the text input client. Flush that now. textInputPlugin.setTextInputEditingState( testView, new TextInputChannel.TextEditState("", 0, 0)); @@ -208,7 +238,15 @@ public void setTextInputEditingState_doesNotRestartOnUnaffectedDevices() { textInputPlugin.setTextInputClient( 0, new TextInputChannel.Configuration( - false, false, true, TextInputChannel.TextCapitalization.NONE, null, null, null)); + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + null, + null)); // There's a pending restart since we initialized the text input client. Flush that now. textInputPlugin.setTextInputEditingState( testView, new TextInputChannel.TextEditState("", 0, 0)); @@ -236,7 +274,15 @@ public void setTextInputEditingState_nullInputMethodSubtype() { textInputPlugin.setTextInputClient( 0, new TextInputChannel.Configuration( - false, false, true, TextInputChannel.TextCapitalization.NONE, null, null, null)); + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + null, + null)); // There's a pending restart since we initialized the text input client. Flush that now. textInputPlugin.setTextInputEditingState( testView, new TextInputChannel.TextEditState("", 0, 0)); @@ -262,6 +308,8 @@ public void inputConnection_createsActionFromEnter() throws JSONException { TextInputChannel.TextCapitalization.NONE, new TextInputChannel.InputType(TextInputChannel.TextInputType.TEXT, false, false), null, + null, + null, null)); // There's a pending restart since we initialized the text input client. Flush that now. textInputPlugin.setTextInputEditingState( @@ -331,6 +379,8 @@ public void inputConnection_finishComposingTextUpdatesIMM() throws JSONException TextInputChannel.TextCapitalization.NONE, new TextInputChannel.InputType(TextInputChannel.TextInputType.TEXT, false, false), null, + null, + null, null)); // There's a pending restart since we initialized the text input client. Flush that now. textInputPlugin.setTextInputEditingState( @@ -347,6 +397,116 @@ public void inputConnection_finishComposingTextUpdatesIMM() throws JSONException } } + @Test + public void autofill_onProvideVirtualViewStructure() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; + + FlutterView testView = new FlutterView(RuntimeEnvironment.application); + TextInputPlugin textInputPlugin = + new TextInputPlugin( + testView, mock(DartExecutor.class), mock(PlatformViewsController.class)); + final TextInputChannel.Configuration.Autofill autofill1 = + new TextInputChannel.Configuration.Autofill( + "1", new String[] {"HINT1"}, new TextInputChannel.TextEditState("", 0, 0)); + final TextInputChannel.Configuration.Autofill autofill2 = + new TextInputChannel.Configuration.Autofill( + "2", new String[] {"HINT2", "EXTRA"}, new TextInputChannel.TextEditState("", 0, 0)); + + final TextInputChannel.Configuration config1 = + new TextInputChannel.Configuration( + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + autofill1, + null); + final TextInputChannel.Configuration config2 = + new TextInputChannel.Configuration( + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + autofill2, + null); + + textInputPlugin.setTextInputClient( + 0, + new TextInputChannel.Configuration( + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + autofill1, + new TextInputChannel.Configuration[] {config1, config2})); + + final ViewStructure viewStructure = mock(ViewStructure.class); + final ViewStructure[] children = {mock(ViewStructure.class), mock(ViewStructure.class)}; + + when(viewStructure.newChild(anyInt())) + .thenAnswer(invocation -> children[invocation.getArgumentAt(0, int.class)]); + + textInputPlugin.onProvideAutofillVirtualStructure(viewStructure, 0); + + verify(viewStructure).newChild(0); + verify(viewStructure).newChild(1); + + verify(children[0]).setAutofillId(any(), eq("1".hashCode())); + verify(children[0]).setAutofillHints(aryEq(new String[] {"HINT1"})); + verify(children[1]).setAutofillId(any(), eq("2".hashCode())); + verify(children[1]).setAutofillHints(aryEq(new String[] {"HINT2", "EXTRA"})); + } + + @Test + public void autofill_onProvideVirtualViewStructure_single() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + + FlutterView testView = new FlutterView(RuntimeEnvironment.application); + TextInputPlugin textInputPlugin = + new TextInputPlugin( + testView, mock(DartExecutor.class), mock(PlatformViewsController.class)); + final TextInputChannel.Configuration.Autofill autofill = + new TextInputChannel.Configuration.Autofill( + "1", new String[] {"HINT1"}, new TextInputChannel.TextEditState("", 0, 0)); + + // Autofill should still work without AutofillGroup. + textInputPlugin.setTextInputClient( + 0, + new TextInputChannel.Configuration( + false, + false, + true, + TextInputChannel.TextCapitalization.NONE, + null, + null, + null, + autofill, + null)); + + final ViewStructure viewStructure = mock(ViewStructure.class); + final ViewStructure[] children = {mock(ViewStructure.class)}; + + when(viewStructure.newChild(anyInt())) + .thenAnswer(invocation -> children[invocation.getArgumentAt(0, int.class)]); + + textInputPlugin.onProvideAutofillVirtualStructure(viewStructure, 0); + + verify(viewStructure).newChild(0); + + verify(children[0]).setAutofillId(any(), eq("1".hashCode())); + verify(children[0]).setAutofillHints(aryEq(new String[] {"HINT1"})); + } + @Implements(InputMethodManager.class) public static class TestImm extends ShadowInputMethodManager { private InputMethodSubtype currentInputMethodSubtype; diff --git a/shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java b/shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java new file mode 100644 index 0000000000000..5bbc7b999a7dd --- /dev/null +++ b/shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java @@ -0,0 +1,290 @@ +// 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.view; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ContentResolver; +import android.content.Context; +import android.view.View; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityNodeInfo; +import io.flutter.embedding.engine.systemchannels.AccessibilityChannel; +import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +@Config(manifest = Config.NONE) +@RunWith(RobolectricTestRunner.class) +public class AccessibilityBridgeTest { + + @Test + public void itDescribesNonTextFieldsWithAContentDescription() { + AccessibilityBridge accessibilityBridge = setUpBridge(); + + TestSemanticsNode testSemanticsNode = new TestSemanticsNode(); + testSemanticsNode.label = "Hello, World"; + TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate(); + + accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings); + AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0); + + assertEquals(nodeInfo.getContentDescription(), "Hello, World"); + assertEquals(nodeInfo.getText(), null); + } + + @Test + public void itDescribesTextFieldsWithText() { + AccessibilityBridge accessibilityBridge = setUpBridge(); + + TestSemanticsNode testSemanticsNode = new TestSemanticsNode(); + testSemanticsNode.label = "Hello, World"; + testSemanticsNode.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD); + TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate(); + + accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings); + AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0); + + assertEquals(nodeInfo.getContentDescription(), null); + assertEquals(nodeInfo.getText(), "Hello, World"); + } + + @Test + public void itDoesNotContainADescriptionIfScopesRoute() { + AccessibilityBridge accessibilityBridge = setUpBridge(); + + TestSemanticsNode testSemanticsNode = new TestSemanticsNode(); + testSemanticsNode.label = "Hello, World"; + testSemanticsNode.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE); + TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate(); + + accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings); + AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0); + + assertEquals(nodeInfo.getContentDescription(), null); + assertEquals(nodeInfo.getText(), null); + } + + @Test + public void itUnfocusesPlatformViewWhenPlatformViewGoesAway() { + AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class); + AccessibilityManager mockManager = mock(AccessibilityManager.class); + View mockRootView = mock(View.class); + Context context = mock(Context.class); + when(mockRootView.getContext()).thenReturn(context); + when(context.getPackageName()).thenReturn("test"); + AccessibilityBridge accessibilityBridge = + setUpBridge(mockRootView, mockManager, mockViewEmbedder); + + // Sent a11y tree with platform view. + TestSemanticsNode root = new TestSemanticsNode(); + root.id = 0; + TestSemanticsNode platformView = new TestSemanticsNode(); + platformView.id = 1; + platformView.platformViewId = 42; + root.children.add(platformView); + TestSemanticsUpdate testSemanticsUpdate = root.toUpdate(); + accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings); + + // Set a11y focus to platform view. + View mockView = mock(View.class); + AccessibilityEvent focusEvent = mock(AccessibilityEvent.class); + when(mockViewEmbedder.requestSendAccessibilityEvent(mockView, mockView, focusEvent)) + .thenReturn(true); + when(mockViewEmbedder.getRecordFlutterId(mockView, focusEvent)).thenReturn(42); + when(focusEvent.getEventType()).thenReturn(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + accessibilityBridge.externalViewRequestSendAccessibilityEvent(mockView, mockView, focusEvent); + + // Replace the platform view. + TestSemanticsNode node = new TestSemanticsNode(); + node.id = 2; + root.children.clear(); + root.children.add(node); + testSemanticsUpdate = root.toUpdate(); + when(mockManager.isEnabled()).thenReturn(true); + ViewParent mockParent = mock(ViewParent.class); + when(mockRootView.getParent()).thenReturn(mockParent); + accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings); + + // Check that unfocus event was sent. + ArgumentCaptor eventCaptor = + ArgumentCaptor.forClass(AccessibilityEvent.class); + verify(mockParent, times(2)) + .requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture()); + AccessibilityEvent event = eventCaptor.getAllValues().get(0); + assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); + } + + AccessibilityBridge setUpBridge() { + return setUpBridge(null, null, null, null, null, null); + } + + AccessibilityBridge setUpBridge( + View rootAccessibilityView, + AccessibilityManager accessibilityManager, + AccessibilityViewEmbedder accessibilityViewEmbedder) { + return setUpBridge( + rootAccessibilityView, null, accessibilityManager, null, accessibilityViewEmbedder, null); + } + + AccessibilityBridge setUpBridge( + View rootAccessibilityView, + AccessibilityChannel accessibilityChannel, + AccessibilityManager accessibilityManager, + ContentResolver contentResolver, + AccessibilityViewEmbedder accessibilityViewEmbedder, + PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate) { + if (rootAccessibilityView == null) { + rootAccessibilityView = mock(View.class); + Context context = mock(Context.class); + when(rootAccessibilityView.getContext()).thenReturn(context); + when(context.getPackageName()).thenReturn("test"); + } + if (accessibilityChannel == null) { + accessibilityChannel = mock(AccessibilityChannel.class); + } + if (accessibilityManager == null) { + accessibilityManager = mock(AccessibilityManager.class); + } + if (contentResolver == null) { + contentResolver = mock(ContentResolver.class); + } + if (accessibilityViewEmbedder == null) { + accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class); + } + if (platformViewsAccessibilityDelegate == null) { + platformViewsAccessibilityDelegate = mock(PlatformViewsAccessibilityDelegate.class); + } + return new AccessibilityBridge( + rootAccessibilityView, + accessibilityChannel, + accessibilityManager, + contentResolver, + accessibilityViewEmbedder, + platformViewsAccessibilityDelegate); + } + + /// The encoding for semantics is described in platform_view_android.cc + class TestSemanticsUpdate { + TestSemanticsUpdate(ByteBuffer buffer, String[] strings) { + this.buffer = buffer; + this.strings = strings; + } + + final ByteBuffer buffer; + final String[] strings; + } + + class TestSemanticsNode { + TestSemanticsNode() {} + + void addFlag(AccessibilityBridge.Flag flag) { + flags |= flag.value; + } + + // These fields are declared in the order they should be + // encoded. + int id = 0; + int flags = 0; + int actions = 0; + int maxValueLength = 0; + int currentValueLength = 0; + int textSelectionBase = 0; + int textSelectionExtent = 0; + int platformViewId = -1; + int scrollChildren = 0; + int scrollIndex = 0; + float scrollPosition = 0.0f; + float scrollExtentMax = 0.0f; + float scrollExtentMin = 0.0f; + String label = null; + String value = null; + String increasedValue = null; + String decreasedValue = null; + String hint = null; + int textDirection = 0; + float left = 0.0f; + float top = 0.0f; + float right = 0.0f; + float bottom = 0.0f; + final List children = new ArrayList(); + // custom actions not supported. + + TestSemanticsUpdate toUpdate() { + ArrayList strings = new ArrayList(); + ByteBuffer bytes = ByteBuffer.allocate(1000); + addToBuffer(bytes, strings); + bytes.flip(); + return new TestSemanticsUpdate(bytes, strings.toArray(new String[strings.size()])); + } + + protected void addToBuffer(ByteBuffer bytes, ArrayList strings) { + bytes.putInt(id); + bytes.putInt(flags); + bytes.putInt(actions); + bytes.putInt(maxValueLength); + bytes.putInt(currentValueLength); + bytes.putInt(textSelectionBase); + bytes.putInt(textSelectionExtent); + bytes.putInt(platformViewId); + bytes.putInt(scrollChildren); + bytes.putInt(scrollIndex); + bytes.putFloat(scrollPosition); + bytes.putFloat(scrollExtentMax); + bytes.putFloat(scrollExtentMin); + updateString(label, bytes, strings); + updateString(value, bytes, strings); + updateString(increasedValue, bytes, strings); + updateString(decreasedValue, bytes, strings); + updateString(hint, bytes, strings); + bytes.putInt(textDirection); + bytes.putFloat(left); + bytes.putFloat(top); + bytes.putFloat(right); + bytes.putFloat(bottom); + // transform. + for (int i = 0; i < 16; i++) { + bytes.putFloat(0); + } + // children in traversal order. + bytes.putInt(children.size()); + for (TestSemanticsNode node : children) { + bytes.putInt(node.id); + } + // children in hit test order. + for (TestSemanticsNode node : children) { + bytes.putInt(node.id); + } + // custom actions + bytes.putInt(0); + // child nodes + for (TestSemanticsNode node : children) { + node.addToBuffer(bytes, strings); + } + } + } + + static void updateString(String value, ByteBuffer bytes, ArrayList strings) { + if (value == null) { + bytes.putInt(-1); + } else { + strings.add(value); + bytes.putInt(strings.size() - 1); + } + } +} diff --git a/shell/platform/config.gni b/shell/platform/config.gni new file mode 100644 index 0000000000000..3684094eb7927 --- /dev/null +++ b/shell/platform/config.gni @@ -0,0 +1,8 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +declare_args() { + # Whether or not to include desktop embedding targets in the build. + enable_desktop_embeddings = !is_fuchsia_host +} diff --git a/shell/platform/darwin/BUILD.gn b/shell/platform/darwin/BUILD.gn index bfc29dfe79d76..cace8efb9a887 100644 --- a/shell/platform/darwin/BUILD.gn +++ b/shell/platform/darwin/BUILD.gn @@ -4,6 +4,7 @@ assert(is_mac || is_ios) +import("//flutter/shell/platform/config.gni") import("//flutter/testing/testing.gni") group("darwin") { @@ -13,9 +14,10 @@ group("darwin") { ] } if (is_mac) { - deps = [ - "macos", - ] + deps = [] + if (enable_desktop_embeddings) { + deps += [ "macos" ] + } } } diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 8fd88147c7e64..7f5870a043128 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -37,10 +37,6 @@ _flutter_framework_headers = [ _flutter_framework_headers_copy_dir = "$_flutter_framework_dir/Headers" -# TODO(54502): move this variable into //build/config/ios/ios_sdk.gni -# Version of iOS that we're targeting for tests. -ios_testing_deployment_target = "13.0" - source_set("flutter_framework_source") { visibility = [ ":*" ] cflags_objc = flutter_cflags_objc @@ -150,89 +146,6 @@ source_set("flutter_framework_source") { ] } -ocmock_path = "../../../../../third_party/ocmock/Source" - -# TODO(54503): Clone the OCMock repository so we can add a BUILD.gn to it. -static_library("ocmock") { - configs -= [ "//build/config/compiler:chromium_code" ] - cflags = [ - "-fvisibility=default", - "-mios-simulator-version-min=$ios_testing_deployment_target", - "-Wno-misleading-indentation", - ] - sources = [ - "$ocmock_path/OCMock/NSInvocation+OCMAdditions.h", - "$ocmock_path/OCMock/NSInvocation+OCMAdditions.m", - "$ocmock_path/OCMock/NSMethodSignature+OCMAdditions.h", - "$ocmock_path/OCMock/NSMethodSignature+OCMAdditions.m", - "$ocmock_path/OCMock/NSNotificationCenter+OCMAdditions.h", - "$ocmock_path/OCMock/NSNotificationCenter+OCMAdditions.m", - "$ocmock_path/OCMock/NSObject+OCMAdditions.h", - "$ocmock_path/OCMock/NSObject+OCMAdditions.m", - "$ocmock_path/OCMock/NSValue+OCMAdditions.h", - "$ocmock_path/OCMock/NSValue+OCMAdditions.m", - "$ocmock_path/OCMock/OCClassMockObject.h", - "$ocmock_path/OCMock/OCClassMockObject.m", - "$ocmock_path/OCMock/OCMArg.h", - "$ocmock_path/OCMock/OCMArg.m", - "$ocmock_path/OCMock/OCMArgAction.h", - "$ocmock_path/OCMock/OCMArgAction.m", - "$ocmock_path/OCMock/OCMBlockArgCaller.h", - "$ocmock_path/OCMock/OCMBlockArgCaller.m", - "$ocmock_path/OCMock/OCMBlockCaller.h", - "$ocmock_path/OCMock/OCMBlockCaller.m", - "$ocmock_path/OCMock/OCMBoxedReturnValueProvider.h", - "$ocmock_path/OCMock/OCMBoxedReturnValueProvider.m", - "$ocmock_path/OCMock/OCMConstraint.h", - "$ocmock_path/OCMock/OCMConstraint.m", - "$ocmock_path/OCMock/OCMExceptionReturnValueProvider.h", - "$ocmock_path/OCMock/OCMExceptionReturnValueProvider.m", - "$ocmock_path/OCMock/OCMExpectationRecorder.h", - "$ocmock_path/OCMock/OCMExpectationRecorder.m", - "$ocmock_path/OCMock/OCMFunctions.h", - "$ocmock_path/OCMock/OCMFunctions.m", - "$ocmock_path/OCMock/OCMFunctionsPrivate.h", - "$ocmock_path/OCMock/OCMIndirectReturnValueProvider.h", - "$ocmock_path/OCMock/OCMIndirectReturnValueProvider.m", - "$ocmock_path/OCMock/OCMInvocationExpectation.h", - "$ocmock_path/OCMock/OCMInvocationExpectation.m", - "$ocmock_path/OCMock/OCMInvocationMatcher.h", - "$ocmock_path/OCMock/OCMInvocationMatcher.m", - "$ocmock_path/OCMock/OCMInvocationStub.h", - "$ocmock_path/OCMock/OCMInvocationStub.m", - "$ocmock_path/OCMock/OCMLocation.h", - "$ocmock_path/OCMock/OCMLocation.m", - "$ocmock_path/OCMock/OCMMacroState.h", - "$ocmock_path/OCMock/OCMMacroState.m", - "$ocmock_path/OCMock/OCMNotificationPoster.h", - "$ocmock_path/OCMock/OCMNotificationPoster.m", - "$ocmock_path/OCMock/OCMObserverRecorder.h", - "$ocmock_path/OCMock/OCMObserverRecorder.m", - "$ocmock_path/OCMock/OCMPassByRefSetter.h", - "$ocmock_path/OCMock/OCMPassByRefSetter.m", - "$ocmock_path/OCMock/OCMRealObjectForwarder.h", - "$ocmock_path/OCMock/OCMRealObjectForwarder.m", - "$ocmock_path/OCMock/OCMRecorder.h", - "$ocmock_path/OCMock/OCMRecorder.m", - "$ocmock_path/OCMock/OCMReturnValueProvider.h", - "$ocmock_path/OCMock/OCMReturnValueProvider.m", - "$ocmock_path/OCMock/OCMStubRecorder.h", - "$ocmock_path/OCMock/OCMStubRecorder.m", - "$ocmock_path/OCMock/OCMVerifier.h", - "$ocmock_path/OCMock/OCMVerifier.m", - "$ocmock_path/OCMock/OCMock.h", - "$ocmock_path/OCMock/OCMockObject.h", - "$ocmock_path/OCMock/OCMockObject.m", - "$ocmock_path/OCMock/OCObserverMockObject.h", - "$ocmock_path/OCMock/OCObserverMockObject.m", - "$ocmock_path/OCMock/OCPartialMockObject.h", - "$ocmock_path/OCMock/OCPartialMockObject.m", - "$ocmock_path/OCMock/OCProtocolMockObject.h", - "$ocmock_path/OCMock/OCProtocolMockObject.m", - ] - include_dirs = [ "$ocmock_path" ] -} - ios_test_flutter_path = rebase_path("$root_out_dir/libios_test_flutter.dylib") platform_frameworks_path = "$ios_sdk_path/../../Library/Frameworks/" @@ -266,11 +179,10 @@ shared_library("ios_test_flutter") { ] deps = [ ":flutter_framework_source", - ":ocmock", "//flutter/shell/platform/darwin/common:framework_shared", + "//third_party/ocmock:ocmock", "//third_party/skia", ] - include_dirs = [ "$ocmock_path" ] public_configs = [ "//flutter:config" ] } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index c3302194b039a..35cf9553b0c61 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -534,6 +534,11 @@ - (void)updateEditingClient:(int)client withState:(NSDictionary*)state { arguments:@[ @(client), state ]]; } +- (void)updateEditingClient:(int)client withState:(NSDictionary*)state withTag:(NSString*)tag { + [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingStateWithTag" + arguments:@[ @(client), @{tag : state} ]]; +} + - (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state withClient:(int)client withPosition:(NSDictionary*)position { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegateTest.m b/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegateTest.m index 4ce64acfc7793..2a5e053707b3b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegateTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegateTest.m @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import #import #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h" +#import "third_party/ocmock/Source/OCMock/OCMock.h" FLUTTER_ASSERT_ARC diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h index 8446a79946d63..033a196696127 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h @@ -30,6 +30,7 @@ typedef NS_ENUM(NSInteger, FlutterFloatingCursorDragState) { @protocol FlutterTextInputDelegate - (void)updateEditingClient:(int)client withState:(NSDictionary*)state; +- (void)updateEditingClient:(int)client withState:(NSDictionary*)state withTag:(NSString*)tag; - (void)performAction:(FlutterTextInputAction)action withClient:(int)client; - (void)updateFloatingCursor:(FlutterFloatingCursorDragState)state withClient:(int)client diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h index 3677c0f78284a..c5571ee10e0b3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h @@ -36,9 +36,6 @@ @end /** A range of text in the buffer of a Flutter text editing widget. */ -#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG -FLUTTER_EXPORT -#endif @interface FlutterTextRange : UITextRange @property(nonatomic, readonly) NSRange range; @@ -71,6 +68,7 @@ FLUTTER_EXPORT @property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; @property(nonatomic) UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0)); @property(nonatomic) UITextSmartDashesType smartDashesType API_AVAILABLE(ios(11.0)); +@property(nonatomic, copy) UITextContentType textContentType API_AVAILABLE(ios(10.0)); @property(nonatomic, assign) id textInputDelegate; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm index f9ddd6621d6d8..068b8bcb76622 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.mm @@ -88,6 +88,122 @@ static UIReturnKeyType ToUIReturnKeyType(NSString* inputType) { return UIReturnKeyDefault; } +static UITextContentType ToUITextContentType(NSArray* hints) { + if (hints == nil || hints.count == 0) { + return @""; + } + + NSString* hint = hints[0]; + if (@available(iOS 10.0, *)) { + if ([hint isEqualToString:@"addressCityAndState"]) { + return UITextContentTypeAddressCityAndState; + } + + if ([hint isEqualToString:@"addressState"]) { + return UITextContentTypeAddressState; + } + + if ([hint isEqualToString:@"addressCity"]) { + return UITextContentTypeAddressCity; + } + + if ([hint isEqualToString:@"sublocality"]) { + return UITextContentTypeSublocality; + } + + if ([hint isEqualToString:@"streetAddressLine1"]) { + return UITextContentTypeStreetAddressLine1; + } + + if ([hint isEqualToString:@"streetAddressLine2"]) { + return UITextContentTypeStreetAddressLine2; + } + + if ([hint isEqualToString:@"countryName"]) { + return UITextContentTypeCountryName; + } + + if ([hint isEqualToString:@"fullStreetAddress"]) { + return UITextContentTypeFullStreetAddress; + } + + if ([hint isEqualToString:@"postalCode"]) { + return UITextContentTypePostalCode; + } + + if ([hint isEqualToString:@"location"]) { + return UITextContentTypeLocation; + } + + if ([hint isEqualToString:@"creditCardNumber"]) { + return UITextContentTypeCreditCardNumber; + } + + if ([hint isEqualToString:@"email"]) { + return UITextContentTypeEmailAddress; + } + + if ([hint isEqualToString:@"jobTitle"]) { + return UITextContentTypeJobTitle; + } + + if ([hint isEqualToString:@"givenName"]) { + return UITextContentTypeGivenName; + } + + if ([hint isEqualToString:@"middleName"]) { + return UITextContentTypeMiddleName; + } + + if ([hint isEqualToString:@"familyName"]) { + return UITextContentTypeFamilyName; + } + + if ([hint isEqualToString:@"name"]) { + return UITextContentTypeName; + } + + if ([hint isEqualToString:@"namePrefix"]) { + return UITextContentTypeNamePrefix; + } + + if ([hint isEqualToString:@"nameSuffix"]) { + return UITextContentTypeNameSuffix; + } + + if ([hint isEqualToString:@"nickname"]) { + return UITextContentTypeNickname; + } + + if ([hint isEqualToString:@"organizationName"]) { + return UITextContentTypeOrganizationName; + } + + if ([hint isEqualToString:@"telephoneNumber"]) { + return UITextContentTypeTelephoneNumber; + } + } + + if (@available(iOS 11.0, *)) { + if ([hint isEqualToString:@"password"]) { + return UITextContentTypePassword; + } + } + + if (@available(iOS 12.0, *)) { + if ([hint isEqualToString:@"oneTimeCode"]) { + return UITextContentTypeOneTimeCode; + } + } + + return hints[0]; +} + +static NSString* uniqueIdFromDictionary(NSDictionary* dictionary) { + NSDictionary* autofill = dictionary[@"autofill"]; + return autofill == nil ? nil : autofill[@"uniqueIdentifier"]; +} + #pragma mark - FlutterTextPosition @implementation FlutterTextPosition @@ -140,6 +256,10 @@ - (id)copyWithZone:(NSZone*)zone { @end +@interface FlutterTextInputView () +@property(nonatomic, copy) NSString* autofillId; +@end + @implementation FlutterTextInputView { int _textInputClient; const char* _selectionAffinity; @@ -184,6 +304,7 @@ - (void)dealloc { [_markedTextRange release]; [_selectedTextRange release]; [_tokenizer release]; + [_autofillId release]; [super dealloc]; } @@ -241,7 +362,10 @@ - (NSRange)clampSelection:(NSRange)range forText:(NSString*)text { #pragma mark - UIResponder Overrides - (BOOL)canBecomeFirstResponder { - return YES; + // Only the currently focused input field can + // become the first responder. This prevents iOS + // from changing focus by itself. + return _textInputClient != 0; } #pragma mark - UITextInput Overrides @@ -600,16 +724,22 @@ - (void)updateEditingState { composingBase = ((FlutterTextPosition*)self.markedTextRange.start).index; composingExtent = ((FlutterTextPosition*)self.markedTextRange.end).index; } - [_textInputDelegate updateEditingClient:_textInputClient - withState:@{ - @"selectionBase" : @(selectionBase), - @"selectionExtent" : @(selectionExtent), - @"selectionAffinity" : @(_selectionAffinity), - @"selectionIsDirectional" : @(false), - @"composingBase" : @(composingBase), - @"composingExtent" : @(composingExtent), - @"text" : [NSString stringWithString:self.text], - }]; + + NSDictionary* state = @{ + @"selectionBase" : @(selectionBase), + @"selectionExtent" : @(selectionExtent), + @"selectionAffinity" : @(_selectionAffinity), + @"selectionIsDirectional" : @(false), + @"composingBase" : @(composingBase), + @"composingExtent" : @(composingExtent), + @"text" : [NSString stringWithString:self.text], + }; + + if (_textInputClient == 0 && _autofillId != nil) { + [_textInputDelegate updateEditingClient:_textInputClient withState:state withTag:_autofillId]; + } else { + [_textInputDelegate updateEditingClient:_textInputClient withState:state]; + } } - (BOOL)hasText { @@ -671,12 +801,15 @@ - (BOOL)accessibilityElementsHidden { @end -@implementation FlutterTextInputPlugin { - FlutterTextInputView* _view; - FlutterTextInputView* _secureView; - FlutterTextInputView* _activeView; - FlutterTextInputViewAccessibilityHider* _inputHider; -} +@interface FlutterTextInputPlugin () +@property(nonatomic, retain) FlutterTextInputView* nonAutofillInputView; +@property(nonatomic, retain) FlutterTextInputView* nonAutofillSecureInputView; +@property(nonatomic, retain) NSMutableArray* inputViews; +@property(nonatomic, assign) FlutterTextInputView* activeView; +@property(nonatomic, retain) FlutterTextInputViewAccessibilityHider* inputHider; +@end + +@implementation FlutterTextInputPlugin @synthesize textInputDelegate = _textInputDelegate; @@ -684,12 +817,13 @@ - (instancetype)init { self = [super init]; if (self) { - _view = [[FlutterTextInputView alloc] init]; - _view.secureTextEntry = NO; - _secureView = [[FlutterTextInputView alloc] init]; - _secureView.secureTextEntry = YES; + _nonAutofillInputView = [[FlutterTextInputView alloc] init]; + _nonAutofillInputView.secureTextEntry = NO; + _nonAutofillInputView = [[FlutterTextInputView alloc] init]; + _nonAutofillSecureInputView.secureTextEntry = YES; + _inputViews = [[NSMutableArray alloc] init]; - _activeView = _view; + _activeView = _nonAutofillInputView; _inputHider = [[FlutterTextInputViewAccessibilityHider alloc] init]; } @@ -698,9 +832,10 @@ - (instancetype)init { - (void)dealloc { [self hideTextInput]; - [_view release]; - [_secureView release]; + [_nonAutofillInputView release]; + [_nonAutofillSecureInputView release]; [_inputHider release]; + [_inputViews release]; [super dealloc]; } @@ -733,58 +868,113 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } - (void)showTextInput { - NSAssert([UIApplication sharedApplication].keyWindow != nullptr, + UIWindow* keyWindow = [UIApplication sharedApplication].keyWindow; + NSAssert(keyWindow != nullptr, @"The application must have a key window since the keyboard client " @"must be part of the responder chain to function"); _activeView.textInputDelegate = _textInputDelegate; - [_inputHider addSubview:_activeView]; - [[UIApplication sharedApplication].keyWindow addSubview:_inputHider]; + if (![_activeView isDescendantOfView:_inputHider]) { + [_inputHider addSubview:_activeView]; + } + [keyWindow addSubview:_inputHider]; [_activeView becomeFirstResponder]; } - (void)hideTextInput { [_activeView resignFirstResponder]; - [_activeView removeFromSuperview]; [_inputHider removeFromSuperview]; } - (void)setTextInputClient:(int)client withConfiguration:(NSDictionary*)configuration { - NSDictionary* inputType = configuration[@"inputType"]; - NSString* keyboardAppearance = configuration[@"keyboardAppearance"]; - if ([configuration[@"obscureText"] boolValue]) { - _activeView = _secureView; + NSArray* fields = configuration[@"fields"]; + NSString* clientUniqueId = uniqueIdFromDictionary(configuration); + bool isSecureTextEntry = [configuration[@"obscureText"] boolValue]; + + if (fields == nil) { + _activeView = isSecureTextEntry ? _nonAutofillSecureInputView : _nonAutofillInputView; + [FlutterTextInputPlugin setupInputView:_activeView withConfiguration:configuration]; + + if (![_activeView isDescendantOfView:_inputHider]) { + [_inputHider addSubview:_activeView]; + } } else { - _activeView = _view; + NSAssert(clientUniqueId != nil, @"The client's unique id can't be null"); + for (FlutterTextInputView* view in _inputViews) { + [view removeFromSuperview]; + } + for (UIView* subview in _inputHider.subviews) { + [subview removeFromSuperview]; + } + + [_inputViews removeAllObjects]; + + for (NSDictionary* field in fields) { + FlutterTextInputView* newInputView = [[[FlutterTextInputView alloc] init] autorelease]; + newInputView.textInputDelegate = _textInputDelegate; + [_inputViews addObject:newInputView]; + + NSString* autofillId = uniqueIdFromDictionary(field); + newInputView.autofillId = autofillId; + + if ([clientUniqueId isEqualToString:autofillId]) { + _activeView = newInputView; + } + + [FlutterTextInputPlugin setupInputView:newInputView withConfiguration:field]; + [_inputHider addSubview:newInputView]; + } } - _activeView.keyboardType = ToUIKeyboardType(inputType); - _activeView.returnKeyType = ToUIReturnKeyType(configuration[@"inputAction"]); - _activeView.autocapitalizationType = ToUITextAutoCapitalizationType(configuration); + [_activeView setTextInputClient:client]; + [_activeView reloadInputViews]; +} + ++ (void)setupInputView:(FlutterTextInputView*)inputView + withConfiguration:(NSDictionary*)configuration { + NSDictionary* inputType = configuration[@"inputType"]; + NSString* keyboardAppearance = configuration[@"keyboardAppearance"]; + NSDictionary* autofill = configuration[@"autofill"]; + + inputView.secureTextEntry = [configuration[@"obscureText"] boolValue]; + inputView.keyboardType = ToUIKeyboardType(inputType); + inputView.returnKeyType = ToUIReturnKeyType(configuration[@"inputAction"]); + inputView.autocapitalizationType = ToUITextAutoCapitalizationType(configuration); + if (@available(iOS 11.0, *)) { NSString* smartDashesType = configuration[@"smartDashesType"]; // This index comes from the SmartDashesType enum in the framework. bool smartDashesIsDisabled = smartDashesType && [smartDashesType isEqualToString:@"0"]; - _activeView.smartDashesType = + inputView.smartDashesType = smartDashesIsDisabled ? UITextSmartDashesTypeNo : UITextSmartDashesTypeYes; NSString* smartQuotesType = configuration[@"smartQuotesType"]; // This index comes from the SmartQuotesType enum in the framework. bool smartQuotesIsDisabled = smartQuotesType && [smartQuotesType isEqualToString:@"0"]; - _activeView.smartQuotesType = + inputView.smartQuotesType = smartQuotesIsDisabled ? UITextSmartQuotesTypeNo : UITextSmartQuotesTypeYes; } if ([keyboardAppearance isEqualToString:@"Brightness.dark"]) { - _activeView.keyboardAppearance = UIKeyboardAppearanceDark; + inputView.keyboardAppearance = UIKeyboardAppearanceDark; } else if ([keyboardAppearance isEqualToString:@"Brightness.light"]) { - _activeView.keyboardAppearance = UIKeyboardAppearanceLight; + inputView.keyboardAppearance = UIKeyboardAppearanceLight; } else { - _activeView.keyboardAppearance = UIKeyboardAppearanceDefault; + inputView.keyboardAppearance = UIKeyboardAppearanceDefault; } NSString* autocorrect = configuration[@"autocorrect"]; - _activeView.autocorrectionType = autocorrect && ![autocorrect boolValue] - ? UITextAutocorrectionTypeNo - : UITextAutocorrectionTypeDefault; - [_activeView setTextInputClient:client]; - [_activeView reloadInputViews]; + inputView.autocorrectionType = autocorrect && ![autocorrect boolValue] + ? UITextAutocorrectionTypeNo + : UITextAutocorrectionTypeDefault; + if (@available(iOS 10.0, *)) { + if (autofill == nil) { + inputView.textContentType = @""; + } else { + inputView.textContentType = ToUITextContentType(autofill[@"hints"]); + [inputView setTextInputState:autofill[@"editingValue"]]; + // An input field needs to be visible in order to get + // autofilled when it's not the one that triggered + // autofill. + inputView.frame = CGRectMake(0, 0, 1, 1); + } + } } - (void)setTextInputEditingState:(NSDictionary*)state { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m index 6610d9fe4d8cd..df6e1a5a2fb8b 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m +++ b/shell/platform/darwin/ios/framework/Source/FlutterTextInputPluginTest.m @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import #import #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" #import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h" #include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" +#import "third_party/ocmock/Source/OCMock/OCMock.h" FLUTTER_ASSERT_ARC @@ -15,6 +15,67 @@ @interface FlutterTextInputPluginTest : XCTestCase @implementation FlutterTextInputPluginTest +- (void)testAutofillInputViews { + // Setup test. + id engine = OCMClassMock([FlutterEngine class]); + FlutterTextInputPlugin* textInputPlugin = [[FlutterTextInputPlugin alloc] init]; + textInputPlugin.textInputDelegate = engine; + + NSDictionary* template = @{ + @"inputType" : @{@"name" : @"TextInuptType.text"}, + @"keyboardAppearance" : @"Brightness.light", + @"obscureText" : @NO, + @"inputAction" : @"TextInputAction.unspecified", + @"smartDashesType" : @"0", + @"smartQuotesType" : @"0", + @"autocorrect" : @YES + }; + + NSMutableDictionary* field1 = [template mutableCopy]; + [field1 setValue:@{ + @"uniqueIdentifier" : @"field1", + @"hints" : @[ @"hint1" ], + @"editingValue" : @{@"text" : @""} + } + forKey:@"autofill"]; + + NSMutableDictionary* field2 = [template mutableCopy]; + [field2 setValue:@{ + @"uniqueIdentifier" : @"field2", + @"hints" : @[ @"hint2" ], + @"editingValue" : @{@"text" : @""} + } + forKey:@"autofill"]; + + NSMutableDictionary* config = [field1 mutableCopy]; + [config setValue:@[ field1, field2 ] forKey:@"fields"]; + + FlutterMethodCall* setClientCall = + [FlutterMethodCall methodCallWithMethodName:@"TextInput.setClient" + arguments:@[ @123, config ]]; + + [textInputPlugin handleMethodCall:setClientCall + result:^(id _Nullable result){ + }]; + + // Find all input views in the input hider view. + NSArray* inputFields = + [[[textInputPlugin textInputView] superview] subviews]; + + XCTAssertEqual(inputFields.count, 2); + + // Find the inactive autofillable input field. + FlutterTextInputView* inactiveView = inputFields[1]; + [inactiveView replaceRange:[FlutterTextRange rangeWithNSRange:NSMakeRange(0, 0)] + withText:@"Autofilled!"]; + + // Verify behavior. + OCMVerify([engine updateEditingClient:0 withState:[OCMArg isNotNil] withTag:@"field2"]); + + // Clean up mocks + [engine stopMocking]; +} + - (void)testAutocorrectionPromptRectAppears { // Setup test. id engine = OCMClassMock([FlutterEngine class]); diff --git a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm index b067ee7920fc1..21bd5611beee1 100644 --- a/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm +++ b/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm @@ -1,8 +1,8 @@ -#import #import #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" #import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h" +#import "third_party/ocmock/Source/OCMock/OCMock.h" FLUTTER_ASSERT_ARC @@ -38,4 +38,30 @@ - (void)testCreate { XCTAssertNotNil(object); } +- (void)testSetChildren { + fml::WeakPtrFactory factory( + new flutter::MockAccessibilityBridge()); + fml::WeakPtr bridge = factory.GetWeakPtr(); + SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0]; + SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1]; + parent.children = @[ child ]; + XCTAssertEqual(parent, child.parent); + parent.children = @[]; + XCTAssertNil(child.parent); +} + +- (void)testReplaceChildAtIndex { + fml::WeakPtrFactory factory( + new flutter::MockAccessibilityBridge()); + fml::WeakPtr bridge = factory.GetWeakPtr(); + SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0]; + SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1]; + SemanticsObject* child2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2]; + parent.children = @[ child1 ]; + [parent replaceChildAtIndex:0 withChild:child2]; + XCTAssertNil(child1.parent); + XCTAssertEqual(parent, child2.parent); + XCTAssertEqualObjects(parent.children, @[ child2 ]); +} + @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index e9c26be8e0b32..4813ab8fe2279 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -405,7 +405,7 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase { FlutterPointerEvent flutterEvent = { .struct_size = sizeof(flutterEvent), .phase = phase, - .timestamp = static_cast(event.timestamp * NSEC_PER_MSEC), + .timestamp = static_cast(event.timestamp * USEC_PER_SEC), .x = locationInBackingCoordinates.x, .y = -locationInBackingCoordinates.y, // convertPointToBacking makes this negative. .device_kind = kFlutterPointerDeviceKindMouse, diff --git a/shell/platform/glfw/config.gni b/shell/platform/glfw/config.gni index 3e636c8239143..36e4b0f700540 100644 --- a/shell/platform/glfw/config.gni +++ b/shell/platform/glfw/config.gni @@ -9,5 +9,5 @@ declare_args() { # but it can be enabled for supported platforms (Windows, macOS, and Linux) # as an extra build artifact with this flag. The native toolkit shell will # still be built as well. - build_glfw_shell = (is_linux || is_win) && current_toolchain == host_toolchain + build_glfw_shell = is_linux && current_toolchain == host_toolchain } diff --git a/shell/platform/linux/config/BUILD.gn b/shell/platform/linux/config/BUILD.gn index d188f2dd6389b..16a8bbc860c9c 100644 --- a/shell/platform/linux/config/BUILD.gn +++ b/shell/platform/linux/config/BUILD.gn @@ -3,9 +3,12 @@ # found in the LICENSE file. import("//build/config/linux/pkg_config.gni") +import("//flutter/shell/platform/glfw/config.gni") -pkg_config("x11") { - packages = [ "x11" ] +if (build_glfw_shell) { + pkg_config("x11") { + packages = [ "x11" ] + } } pkg_config("gtk") { diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 5de1a712a482e..ce8c6c0ec7fe1 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -49,6 +49,8 @@ source_set("flutter_windows_source") { "keyboard_hook_handler.h", "platform_handler.cc", "platform_handler.h", + "string_conversion.cc", + "string_conversion.h", "text_input_plugin.cc", "text_input_plugin.h", "win32_flutter_window.cc", @@ -108,6 +110,7 @@ executable("flutter_windows_unittests") { sources = [ "dpi_utils_unittests.cc", + "string_conversion_unittests.cc", "testing/win32_flutter_window_test.cc", "testing/win32_flutter_window_test.h", "testing/win32_window_test.cc", diff --git a/shell/platform/windows/platform_handler.cc b/shell/platform/windows/platform_handler.cc index f1540a9819016..c4b83130a785b 100644 --- a/shell/platform/windows/platform_handler.cc +++ b/shell/platform/windows/platform_handler.cc @@ -7,8 +7,11 @@ #include #include +#include #include "flutter/shell/platform/common/cpp/json_method_codec.h" +#include "flutter/shell/platform/windows/string_conversion.h" +#include "flutter/shell/platform/windows/win32_flutter_window.h" static constexpr char kChannelName[] = "flutter/platform"; @@ -18,11 +21,182 @@ static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData"; static constexpr char kTextPlainFormat[] = "text/plain"; static constexpr char kTextKey[] = "text"; -static constexpr char kUnknownClipboardFormatError[] = - "Unknown clipboard format error"; +static constexpr char kClipboardError[] = "Clipboard error"; +static constexpr char kUnknownClipboardFormatMessage[] = + "Unknown clipboard format"; namespace flutter { +namespace { + +// A scoped wrapper for GlobalAlloc/GlobalFree. +class ScopedGlobalMemory { + public: + // Allocates |bytes| bytes of global memory with the given flags. + ScopedGlobalMemory(unsigned int flags, size_t bytes) { + memory_ = ::GlobalAlloc(flags, bytes); + if (!memory_) { + std::cerr << "Unable to allocate global memory: " << ::GetLastError(); + } + } + + ~ScopedGlobalMemory() { + if (memory_) { + if (::GlobalFree(memory_) != nullptr) { + std::cerr << "Failed to free global allocation: " << ::GetLastError(); + } + } + } + + // Prevent copying. + ScopedGlobalMemory(ScopedGlobalMemory const&) = delete; + ScopedGlobalMemory& operator=(ScopedGlobalMemory const&) = delete; + + // Returns the memory pointer, which will be nullptr if allocation failed. + void* get() { return memory_; } + + void* release() { + void* memory = memory_; + memory_ = nullptr; + return memory; + } + + private: + HGLOBAL memory_; +}; + +// A scoped wrapper for GlobalLock/GlobalUnlock. +class ScopedGlobalLock { + public: + // Attempts to acquire a global lock on |memory| for the life of this object. + ScopedGlobalLock(HGLOBAL memory) { + source_ = memory; + if (memory) { + locked_memory_ = ::GlobalLock(memory); + if (!locked_memory_) { + std::cerr << "Unable to acquire global lock: " << ::GetLastError(); + } + } + } + + ~ScopedGlobalLock() { + if (locked_memory_) { + if (!::GlobalUnlock(source_)) { + DWORD error = ::GetLastError(); + if (error != NO_ERROR) { + std::cerr << "Unable to release global lock: " << ::GetLastError(); + } + } + } + } + + // Prevent copying. + ScopedGlobalLock(ScopedGlobalLock const&) = delete; + ScopedGlobalLock& operator=(ScopedGlobalLock const&) = delete; + + // Returns the locked memory pointer, which will be nullptr if acquiring the + // lock failed. + void* get() { return locked_memory_; } + + private: + HGLOBAL source_; + void* locked_memory_; +}; + +// A Clipboard wrapper that automatically closes the clipboard when it goes out +// of scope. +class ScopedClipboard { + public: + ScopedClipboard(); + ~ScopedClipboard(); + + // Prevent copying. + ScopedClipboard(ScopedClipboard const&) = delete; + ScopedClipboard& operator=(ScopedClipboard const&) = delete; + + // Attempts to open the clipboard for the given window, returning true if + // successful. + bool Open(HWND window); + + // Returns true if there is string data available to get. + bool HasString(); + + // Returns string data from the clipboard. + // + // If getting a string fails, returns no value. Get error information with + // ::GetLastError(). + // + // Open(...) must have succeeded to call this method. + std::optional GetString(); + + // Sets the string content of the clipboard, returning true on success. + // + // On failure, get error information with ::GetLastError(). + // + // Open(...) must have succeeded to call this method. + bool SetString(const std::wstring string); + + private: + bool opened_ = false; +}; + +ScopedClipboard::ScopedClipboard() {} + +ScopedClipboard::~ScopedClipboard() { + if (opened_) { + ::CloseClipboard(); + } +} + +bool ScopedClipboard::Open(HWND window) { + opened_ = ::OpenClipboard(window); + return opened_; +} + +bool ScopedClipboard::HasString() { + // Allow either plain text format, since getting data will auto-interpolate. + return ::IsClipboardFormatAvailable(CF_UNICODETEXT) || + ::IsClipboardFormatAvailable(CF_TEXT); +} + +std::optional ScopedClipboard::GetString() { + assert(opened_); + + HANDLE data = ::GetClipboardData(CF_UNICODETEXT); + if (data == nullptr) { + return std::nullopt; + } + ScopedGlobalLock locked_data(data); + if (!locked_data.get()) { + return std::nullopt; + } + return std::optional(static_cast(locked_data.get())); +} + +bool ScopedClipboard::SetString(const std::wstring string) { + assert(opened_); + if (!::EmptyClipboard()) { + return false; + } + size_t null_terminated_byte_count = + sizeof(decltype(string)::traits_type::char_type) * (string.size() + 1); + ScopedGlobalMemory destination_memory(GMEM_MOVEABLE, + null_terminated_byte_count); + ScopedGlobalLock locked_memory(destination_memory.get()); + if (!locked_memory.get()) { + return false; + } + memcpy(locked_memory.get(), string.c_str(), null_terminated_byte_count); + if (!::SetClipboardData(CF_UNICODETEXT, locked_memory.get())) { + return false; + } + // The clipboard now owns the global memory. + destination_memory.release(); + return true; +} + +} // namespace + PlatformHandler::PlatformHandler(flutter::BinaryMessenger* messenger, Win32FlutterWindow* window) : channel_(std::make_unique>( @@ -47,76 +221,65 @@ void PlatformHandler::HandleMethodCall( const rapidjson::Value& format = method_call.arguments()[0]; if (strcmp(format.GetString(), kTextPlainFormat) != 0) { - result->Error(kUnknownClipboardFormatError, - "Windows clipboard API only supports text."); + result->Error(kClipboardError, kUnknownClipboardFormatMessage); return; } - auto clipboardData = GetClipboardString(); - - if (clipboardData.empty()) { - result->Error(kUnknownClipboardFormatError, - "Failed to retrieve clipboard data from win32 api."); + ScopedClipboard clipboard; + if (!clipboard.Open(window_->GetWindowHandle())) { + rapidjson::Document error_code; + error_code.SetInt(::GetLastError()); + result->Error(kClipboardError, "Unable to open clipboard", &error_code); return; } + if (!clipboard.HasString()) { + rapidjson::Document null; + result->Success(&null); + return; + } + std::optional clipboard_string = clipboard.GetString(); + if (!clipboard_string) { + rapidjson::Document error_code; + error_code.SetInt(::GetLastError()); + result->Error(kClipboardError, "Unable to get clipboard data", + &error_code); + return; + } + rapidjson::Document document; document.SetObject(); rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); - document.AddMember(rapidjson::Value(kTextKey, allocator), - rapidjson::Value(clipboardData, allocator), allocator); + document.AddMember( + rapidjson::Value(kTextKey, allocator), + rapidjson::Value(Utf8FromUtf16(*clipboard_string), allocator), + allocator); result->Success(&document); - } else if (method.compare(kSetClipboardDataMethod) == 0) { const rapidjson::Value& document = *method_call.arguments(); rapidjson::Value::ConstMemberIterator itr = document.FindMember(kTextKey); if (itr == document.MemberEnd()) { - result->Error(kUnknownClipboardFormatError, - "Missing text to store on clipboard."); + result->Error(kClipboardError, kUnknownClipboardFormatMessage); + return; + } + + ScopedClipboard clipboard; + if (!clipboard.Open(window_->GetWindowHandle())) { + rapidjson::Document error_code; + error_code.SetInt(::GetLastError()); + result->Error(kClipboardError, "Unable to open clipboard", &error_code); + return; + } + if (!clipboard.SetString(Utf16FromUtf8(itr->value.GetString()))) { + rapidjson::Document error_code; + error_code.SetInt(::GetLastError()); + result->Error(kClipboardError, "Unable to set clipboard data", + &error_code); return; } - SetClipboardString(std::string(itr->value.GetString())); result->Success(); } else { result->NotImplemented(); } } -std::string PlatformHandler::GetClipboardString() { - if (!OpenClipboard(nullptr)) { - return nullptr; - } - - HANDLE data = GetClipboardData(CF_TEXT); - if (data == nullptr) { - CloseClipboard(); - return nullptr; - } - - const char* clipboardData = static_cast(GlobalLock(data)); - - if (clipboardData == nullptr) { - CloseClipboard(); - return nullptr; - } - - auto result = std::string(clipboardData); - GlobalUnlock(data); - CloseClipboard(); - return result; -} - -void PlatformHandler::SetClipboardString(std::string data) { - if (!OpenClipboard(nullptr)) { - return; - } - - auto htext = GlobalAlloc(GMEM_MOVEABLE, data.size()); - - memcpy(GlobalLock(htext), data.c_str(), data.size()); - - SetClipboardData(CF_TEXT, htext); - - CloseClipboard(); -} - } // namespace flutter diff --git a/shell/platform/windows/platform_handler.h b/shell/platform/windows/platform_handler.h index 58fbf55031c87..8cb09a80666a7 100644 --- a/shell/platform/windows/platform_handler.h +++ b/shell/platform/windows/platform_handler.h @@ -29,9 +29,6 @@ class PlatformHandler { // The MethodChannel used for communication with the Flutter engine. std::unique_ptr> channel_; - static std::string GetClipboardString(); - static void SetClipboardString(std::string data); - // A reference to the win32 window. Win32FlutterWindow* window_; }; diff --git a/shell/platform/windows/string_conversion.cc b/shell/platform/windows/string_conversion.cc new file mode 100644 index 0000000000000..1519337a0acba --- /dev/null +++ b/shell/platform/windows/string_conversion.cc @@ -0,0 +1,55 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/windows/string_conversion.h" + +#include + +namespace flutter { + +std::string Utf8FromUtf16(const std::wstring& utf16_string) { + if (utf16_string.empty()) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), + static_cast(utf16_string.length()), nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string.data(), + static_cast(utf16_string.length()), utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} + +std::wstring Utf16FromUtf8(const std::string& utf8_string) { + if (utf8_string.empty()) { + return std::wstring(); + } + int target_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), nullptr, 0); + if (target_length == 0) { + return std::wstring(); + } + std::wstring utf16_string; + utf16_string.resize(target_length); + int converted_length = + ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, utf8_string.data(), + static_cast(utf8_string.length()), + utf16_string.data(), target_length); + if (converted_length == 0) { + return std::wstring(); + } + return utf16_string; +} + +} // namespace flutter diff --git a/shell/platform/windows/string_conversion.h b/shell/platform/windows/string_conversion.h new file mode 100644 index 0000000000000..ce84be2f46f85 --- /dev/null +++ b/shell/platform/windows/string_conversion.h @@ -0,0 +1,22 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_STRING_CONVERSION_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_STRING_CONVERSION_H_ + +#include + +namespace flutter { + +// Converts a string from UTF-16 to UTF-8. Returns an empty string if the +// input is not valid UTF-16. +std::string Utf8FromUtf16(const std::wstring& utf16_string); + +// Converts a string from UTF-8 to UTF-16. Returns an empty string if the +// input is not valid UTF-8. +std::wstring Utf16FromUtf8(const std::string& utf8_string); + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_STRING_CONVERSION_H_ diff --git a/shell/platform/windows/string_conversion_unittests.cc b/shell/platform/windows/string_conversion_unittests.cc new file mode 100644 index 0000000000000..aa8dc967f3c81 --- /dev/null +++ b/shell/platform/windows/string_conversion_unittests.cc @@ -0,0 +1,37 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/windows/string_conversion.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(StringConversion, Utf16FromUtf8Empty) { + EXPECT_EQ(Utf16FromUtf8(""), L""); +} + +TEST(StringConversion, Utf16FromUtf8Ascii) { + EXPECT_EQ(Utf16FromUtf8("abc123"), L"abc123"); +} + +TEST(StringConversion, Utf16FromUtf8Unicode) { + EXPECT_EQ(Utf16FromUtf8("\xe2\x98\x83"), L"\x2603"); +} + +TEST(StringConversion, Utf8FromUtf16Empty) { + EXPECT_EQ(Utf8FromUtf16(L""), ""); +} + +TEST(StringConversion, Utf8FromUtf16Ascii) { + EXPECT_EQ(Utf8FromUtf16(L"abc123"), "abc123"); +} + +TEST(StringConversion, Utf8FromUtf16Unicode) { + EXPECT_EQ(Utf8FromUtf16(L"\x2603"), "\xe2\x98\x83"); +} + +} // namespace testing +} // namespace flutter diff --git a/testing/assertions_skia.cc b/testing/assertions_skia.cc index c070f1c6cc93d..d76b057bf9eb2 100644 --- a/testing/assertions_skia.cc +++ b/testing/assertions_skia.cc @@ -61,15 +61,15 @@ std::ostream& operator<<(std::ostream& os, const SkMatrix& m) { return os; } -std::ostream& operator<<(std::ostream& os, const SkMatrix44& m) { - os << m.get(0, 0) << ", " << m.get(0, 1) << ", " << m.get(0, 2) << ", " - << m.get(0, 3) << std::endl; - os << m.get(1, 0) << ", " << m.get(1, 1) << ", " << m.get(1, 2) << ", " - << m.get(1, 3) << std::endl; - os << m.get(2, 0) << ", " << m.get(2, 1) << ", " << m.get(2, 2) << ", " - << m.get(2, 3) << std::endl; - os << m.get(3, 0) << ", " << m.get(3, 1) << ", " << m.get(3, 2) << ", " - << m.get(3, 3); +std::ostream& operator<<(std::ostream& os, const SkM44& m) { + os << m.rc(0, 0) << ", " << m.rc(0, 1) << ", " << m.rc(0, 2) << ", " + << m.rc(0, 3) << std::endl; + os << m.rc(1, 0) << ", " << m.rc(1, 1) << ", " << m.rc(1, 2) << ", " + << m.rc(1, 3) << std::endl; + os << m.rc(2, 0) << ", " << m.rc(2, 1) << ", " << m.rc(2, 2) << ", " + << m.rc(2, 3) << std::endl; + os << m.rc(3, 0) << ", " << m.rc(3, 1) << ", " << m.rc(3, 2) << ", " + << m.rc(3, 3); return os; } diff --git a/testing/assertions_skia.h b/testing/assertions_skia.h index f1eec1897c426..6049e778b67eb 100644 --- a/testing/assertions_skia.h +++ b/testing/assertions_skia.h @@ -8,6 +8,7 @@ #include #include "third_party/skia/include/core/SkClipOp.h" +#include "third_party/skia/include/core/SkM44.h" #include "third_party/skia/include/core/SkMatrix.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "third_party/skia/include/core/SkPaint.h" @@ -20,7 +21,7 @@ namespace testing { extern std::ostream& operator<<(std::ostream& os, const SkClipOp& o); extern std::ostream& operator<<(std::ostream& os, const SkMatrix& m); -extern std::ostream& operator<<(std::ostream& os, const SkMatrix44& m); +extern std::ostream& operator<<(std::ostream& os, const SkM44& m); extern std::ostream& operator<<(std::ostream& os, const SkVector3& v); extern std::ostream& operator<<(std::ostream& os, const SkVector4& v); extern std::ostream& operator<<(std::ostream& os, const SkRect& r); diff --git a/testing/fuchsia/run_tests.sh b/testing/fuchsia/run_tests.sh index 7fa6d38b68e77..452a064bc678e 100755 --- a/testing/fuchsia/run_tests.sh +++ b/testing/fuchsia/run_tests.sh @@ -10,12 +10,18 @@ # same directory as the script, as well as the `flutter_aot_runner-0.far` and # the `flutter_runner_tests-0.far`. It is written to be run from its own # directory, and will fail if run from other directories or via sym-links. +# +# This script also expects a private key available at: +# "/etc/botanist/keys/id_rsa_infra". set -Ee # The nodes are named blah-blah--four-word-fuchsia-id device_name=${SWARMING_BOT_ID#*--} +# Bot key to pave and ssh the device. +pkey="/etc/botanist/keys/id_rsa_infra" + if [ -z "$device_name" ] then echo "No device found. Aborting." @@ -28,21 +34,27 @@ reboot() { echo "Dumping system logs..." ./fuchsia_ctl -d $device_name ssh \ - -c "log_listener --dump_logs yes" + -c "log_listener --dump_logs yes" \ + --identity-file $pkey # note: this will set an exit code of 255, which we can ignore. - ./fuchsia_ctl -d $device_name ssh -c "dm reboot-recovery" || true + ./fuchsia_ctl -d $device_name ssh -c "dm reboot-recovery" \ + --identity-file $pkey || true } trap reboot EXIT echo "$(date) START:PAVING ------------------------------------------" -./fuchsia_ctl -d $device_name pave -i $1 +ssh-keygen -y -f $pkey > key.pub +./fuchsia_ctl -d $device_name pave -i $1 \ + --public-key "key.pub" echo "$(date) END:PAVING --------------------------------------------" echo "$(date) START:WAIT_DEVICE_READY -------------------------------" for i in {1..10}; do - ./fuchsia_ctl -d $device_name ssh -c "echo up" && break || sleep 15; + ./fuchsia_ctl -d $device_name ssh \ + --identity-file $pkey \ + -c "echo up" && break || sleep 15; done echo "$(date) END:WAIT_DEVICE_READY ---------------------------------" @@ -52,7 +64,9 @@ echo "$(date) START:flutter_runner_tests ----------------------------" ./fuchsia_ctl -d $device_name test \ -f flutter_aot_runner-0.far \ -f flutter_runner_tests-0.far \ - -t flutter_runner_tests + -t flutter_runner_tests \ + --identity-file $pkey \ + --timeout-seconds 300 # TODO(https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=47081) # Re-enable once the crash is resolved @@ -67,17 +81,23 @@ echo "$(date) START:fml_tests ---------------------------------------" ./fuchsia_ctl -d $device_name test \ -f fml_tests-0.far \ -t fml_tests \ - -a "--gtest_filter=-MessageLoop*:Message*:FileTest*" + -a "--gtest_filter=-MessageLoop*:Message*:FileTest*" \ + --identity-file $pkey \ + --timeout-seconds 300 echo "$(date) START:flow_tests --------------------------------------" ./fuchsia_ctl -d $device_name test \ -f flow_tests-0.far \ - -t flow_tests + -t flow_tests \ + --identity-file $pkey \ + --timeout-seconds 300 echo "$(date) START:runtime_tests -----------------------------------" ./fuchsia_ctl -d $device_name test \ -f runtime_tests-0.far \ - -t runtime_tests + -t runtime_tests \ + --identity-file $pkey \ + --timeout-seconds 300 # TODO(https://github.com/flutter/flutter/issues/53399): Re-enable # OnServiceProtocolGetSkSLsWorks and CanLoadSkSLsFromAsset once they pass on @@ -86,4 +106,7 @@ echo "$(date) START:shell_tests -------------------------------------" ./fuchsia_ctl -d $device_name test \ -f shell_tests-0.far \ -t shell_tests \ - -a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks:ShellTest.CanLoadSkSLsFromAsset" + -a "--gtest_filter=-ShellTest.CacheSkSLWorks:ShellTest.SetResourceCacheSize*:ShellTest.OnServiceProtocolGetSkSLsWorks:ShellTest.CanLoadSkSLsFromAsset" \ + --identity-file $pkey \ + --timeout-seconds 300 + diff --git a/testing/ios/IosUnitTests/build_and_run_tests.sh b/testing/ios/IosUnitTests/build_and_run_tests.sh index 0c586192d9490..833459c019d45 100755 --- a/testing/ios/IosUnitTests/build_and_run_tests.sh +++ b/testing/ios/IosUnitTests/build_and_run_tests.sh @@ -1,5 +1,7 @@ #!/bin/sh +set -e + FLUTTER_ENGINE=ios_debug_sim_unopt if [ $# -eq 1 ]; then diff --git a/testing/mock_canvas.cc b/testing/mock_canvas.cc index 300f2d4288c0d..a29d0d77e999d 100644 --- a/testing/mock_canvas.cc +++ b/testing/mock_canvas.cc @@ -60,11 +60,17 @@ void MockCanvas::didConcat(const SkMatrix& matrix) { draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrixData{matrix}}); } +#ifdef SK_SUPPORT_LEGACY_DIDCONCAT44 void MockCanvas::didConcat44(const SkScalar matrix[]) { - SkMatrix44 m44; - m44.setColMajor(matrix); + SkM44 m44 = SkM44::ColMajor(matrix); draw_calls_.emplace_back(DrawCall{current_layer_, ConcatMatrix44Data{m44}}); } +#else +void MockCanvas::didConcat44(const SkM44& matrix) { + draw_calls_.emplace_back( + DrawCall{current_layer_, ConcatMatrix44Data{matrix}}); +} +#endif void MockCanvas::didScale(SkScalar x, SkScalar y) { SkMatrix m; diff --git a/testing/mock_canvas.h b/testing/mock_canvas.h index 2957783dbe2b5..72f5f1f9ad8c0 100644 --- a/testing/mock_canvas.h +++ b/testing/mock_canvas.h @@ -16,7 +16,7 @@ #include "third_party/skia/include/core/SkClipOp.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkImageFilter.h" -#include "third_party/skia/include/core/SkMatrix44.h" +#include "third_party/skia/include/core/SkM44.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/core/SkRRect.h" #include "third_party/skia/include/core/SkRect.h" @@ -57,7 +57,7 @@ class MockCanvas : public SkCanvasVirtualEnforcer { }; struct ConcatMatrix44Data { - SkMatrix44 matrix; + SkM44 matrix; }; struct SetMatrixData { @@ -145,7 +145,11 @@ class MockCanvas : public SkCanvasVirtualEnforcer { void willRestore() override; void didRestore() override {} void didConcat(const SkMatrix& matrix) override; +#ifdef SK_SUPPORT_LEGACY_DIDCONCAT44 void didConcat44(const SkScalar matrix[]) override; +#else + void didConcat44(const SkM44&) override; +#endif void didScale(SkScalar x, SkScalar y) override; void didTranslate(SkScalar x, SkScalar y) override; void didSetMatrix(const SkMatrix& matrix) override; diff --git a/third_party/tonic/typed_data/typed_list.cc b/third_party/tonic/typed_data/typed_list.cc index 3a491c005ec85..ffe662591dea4 100644 --- a/third_party/tonic/typed_data/typed_list.cc +++ b/third_party/tonic/typed_data/typed_list.cc @@ -69,7 +69,9 @@ template void DartConverter>::SetReturnValue( Dart_NativeArguments args, TypedList val) { - Dart_SetReturnValue(args, val.dart_handle()); + Dart_Handle result = val.dart_handle(); + val.Release(); // Must release acquired typed data before calling Dart API. + Dart_SetReturnValue(args, result); } template diff --git a/tools/gn b/tools/gn index 6d47127ca30fe..6bdf3f74d0b14 100755 --- a/tools/gn +++ b/tools/gn @@ -250,9 +250,14 @@ def to_gn_args(args): gn_args['dart_platform_sdk'] = not args.full_dart_sdk gn_args['full_dart_sdk'] = args.full_dart_sdk - if sys.platform == 'darwin': - if args.build_glfw_shell: - gn_args['build_glfw_shell'] = True + # Desktop embeddings can have more dependencies than the engine library, + # which can be problematic in some build environments (e.g., building on + # Linux will bring in pkg-config dependencies at generation time). These + # flags allow preventing those those targets from being part of the build + # tree. + gn_args['enable_desktop_embeddings'] = not args.disable_desktop_embeddings + if args.build_glfw_shell is not None: + gn_args['build_glfw_shell'] = args.build_glfw_shell gn_args['stripped_symbols'] = args.stripped @@ -340,8 +345,12 @@ def parse_args(args): help='The IDE files to generate using GN. Use `gn gen help` and look for the --ide flag to' + ' see supported IDEs. If this flag is not specified, a platform specific default is selected.') - parser.add_argument('--build-glfw-shell', dest='build_glfw_shell', default=False, action='store_true', - help='Force building the GLFW shell on desktop platforms where it is not built by default.') + parser.add_argument('--disable-desktop-embeddings', default=False, action='store_true', + help='Do not include desktop embeddings in the build.') + parser.add_argument('--build-glfw-shell', action='store_const', const=True, + help='Build the GLFW shell on supported platforms where it is not built by default.') + parser.add_argument('--no-build-glfw-shell', dest='build_glfw_shell', action='store_const', const=False, + help='Do not build the GLFW shell on platforms where it is built by default.') parser.add_argument('--bitcode', default=False, action='store_true', help='Enable bitcode for iOS targets. On debug runtime modes, this will be a marker only.')